如何在AspNet Core中处理空查询参数?

假设我们有一个查询?key1=foo1&key1=foo2&key2=&key3=null
解析它时,我希望解析该URL时会有某种Dictionary>的结果,例如:

  • key1:[“foo1”,“foo2”]这应该是同一键
  • 下的多个值
  • key2:[“”]这应该是一个空字符串
  • key3:[“null”]这应该是一个字符串,据我所知,URL中的null只是字面的

  • 我的问题是:我应该如何处理空查询参数?

    注意:我无法简单地定义查询参数,并假设不存在的查询参数为null。但是我认为,如果需要,在显式查询参数中应将null视为有效值。

    根据此线程:How to send NULL in HTTP query string?
    标准是传递编码的空值:请参见https://www.w3schools.com/tags/ref_urlencode.asp

    因此,如果我想传递一个空值,我应该做类似的事情:?key1=foo1&key1=foo2&key2=&key3=%00
    问题是我不知道如何解码,因此%00被解析为空值。

    我尝试了以下方法:
    public Dictionary<string, List<string>> CreateFromQuery(string query)
    {
        if (query == null)
        {
            return new Dictionary<string, List<string>>();
        }
    
        var queryDictionary = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(query);
    
        var result = queryDictionary.ToDictionary(kv => kv.Key, kv => kv.Value.ToList());
        return result;
    }
    

    但是%00转换为"\0"字符串,而不是null

    之前执行var decodedQuery= HttpUtility.UrlDecode(query);似乎也没有任何区别。

    UPDATE1 :在Kacper和Chris Pratt发表评论(谢谢大家)之后,我暂时接受了Kacper的第二条建议,因为我认为在某些情况下请求者希望区分空查询参数,空查询参数和不存在的查询是很有趣的。参数。

    这是我当前的实现:
    public class QueryParserFactory
        : IQueryParseable
    {
        public Dictionary<string, List<string>> CreateFromQuery(string query)
        {
            if (query == null)
            {
                return new Dictionary<string, List<string>>();
            }
    
            var queryDecoded = HttpUtility.UrlDecode(query);
    
            var queryDictionary = QueryHelpers.ParseQuery(queryDecoded);
    
            var result = queryDictionary
                .ToDictionary(
                    kv => kv.Key,
                    kv => kv.Value.Select(s => s == "\0" ? null : s).ToList());
            return result;
        }
    }
    

    如果有人感兴趣,下面是我可以想到的所有单元测试:
    public static class CreateFromQueryTests
    {
        public class Given_An_Empty_Query_String_When_Creating_From_A_Query
            : Given_When_Then_Test
        {
            private QueryParserFactory _sut;
            private Dictionary<string, List<string>> _result;
    
            protected override void Given()
            {
                _sut = new QueryParserFactory();
            }
    
            protected override void When()
            {
                _result = _sut.CreateFromQuery("");
            }
    
            [Fact]
            public void Then_It_Should_Return_A_Valid_Result()
            {
                _result.Should().NotBeNull();
            }
    
            [Fact]
            public void Then_It_Should_Not_Have_Any_Key()
            {
                _result.Keys.Count.Should().Be(0);
            }
    
            [Fact]
            public void Then_It_Should_Not_Have_Any_Items_In_Dictionary()
            {
                _result.Count.Should().Be(0);
            }
        }
    
        public class Given_A_Query_String_With_Empty_Values_When_Creating_From_A_Query
            : Given_When_Then_Test
        {
            private QueryParserFactory _sut;
            private Dictionary<string, List<string>> _result;
            private List<string> _expectedValueForKey1;
            private List<string> _expectedValueForKey2;
    
            protected override void Given()
            {
                _expectedValueForKey1 = new List<string>
                {
                    string.Empty
                };
    
                _expectedValueForKey2 = new List<string>
                {
                    string.Empty
                };
    
                _sut = new QueryParserFactory();
            }
    
            protected override void When()
            {
                _result = _sut.CreateFromQuery("?key1=&key2=");
            }
    
            [Fact]
            public void Then_It_Should_Return_A_Valid_Result()
            {
                _result.Should().NotBeNull();
            }
    
            [Fact]
            public void Then_It_Should_Have_Key_For_All_Fulfilled_Parameters()
            {
                _result.Keys.Count.Should().Be(2);
            }
    
            [Fact]
            public void Then_It_Should_Have_Empty_Value_For_The_First_Key_Parameter()
            {
                _result["key1"].Should().BeEquivalentTo(_expectedValueForKey1);
            }
    
            [Fact]
            public void Then_It_Should_Have_Empty_Value_For_The_Second_Key_Parameter()
            {
                _result["key2"].Should().BeEquivalentTo(_expectedValueForKey2);
            }
        }
    
        public class Given_A_Query_String_With_Single_Values_When_Creating_From_A_Query
            : Given_When_Then_Test
        {
            private QueryParserFactory _sut;
            private Dictionary<string, List<string>> _result;
            private List<string> _expectedValueForKey1;
            private List<string> _expectedValueForKey2;
    
            protected override void Given()
            {
                _expectedValueForKey1 = new List<string>()
                {
                    "value1"
                };
    
                _expectedValueForKey2 = new List<string>()
                {
                    "value2"
                };
    
                _sut = new QueryParserFactory();
            }
    
            protected override void When()
            {
                _result = _sut.CreateFromQuery("?key1=value1&key2=value2");
            }
    
            [Fact]
            public void Then_It_Should_Return_A_Valid_Result()
            {
                _result.Should().NotBeNull();
            }
    
            [Fact]
            public void Then_It_Should_Have_Key_For_All_Fulfilled_Parameters()
            {
                _result.Keys.Count.Should().Be(2);
            }
    
            [Fact]
            public void Then_It_Should_Have_The_Correct_Multiple_Values_For_Keys_With_Multiple_Parameters()
            {
                _result["key1"].Should().BeEquivalentTo(_expectedValueForKey1);
            }
    
            [Fact]
            public void Then_It_Should_Have_The_Correct_Single_Value_For_Keys_With_One_Parameter()
            {
                _result["key2"].Should().BeEquivalentTo(_expectedValueForKey2);
            }
    
            [Fact]
            public void Then_It_Should_Not_Have_Entries_For_Inexistent_Parameters()
            {
                _result.TryGetValue("key3", out List<string> _).Should().BeFalse();
            }
        }
    
        public class Given_A_Query_String_With_Multiple_Values_For_The_Same_Key_When_Creating_From_A_Query
            : Given_When_Then_Test
        {
            private QueryParserFactory _sut;
            private Dictionary<string, List<string>> _result;
            private List<string> _expectedValueForKey1;
    
            protected override void Given()
            {
                _expectedValueForKey1 = new List<string>()
                {
                    "value1",
                    "value2",
                    "value3"
                };
    
                _sut = new QueryParserFactory();
            }
    
            protected override void When()
            {
                _result = _sut.CreateFromQuery("?key1=value1&key1=value2&key1=value3");
            }
    
            [Fact]
            public void Then_It_Should_Return_A_Valid_Result()
            {
                _result.Should().NotBeNull();
            }
    
            [Fact]
            public void Then_It_Should_Have_Only_One_Key()
            {
                _result.Keys.Count.Should().Be(1);
            }
    
            [Fact]
            public void Then_It_Should_Have_The_Correct_Multiple_Values_For_Keys_With_Multiple_Parameters()
            {
                _result["key1"].Should().BeEquivalentTo(_expectedValueForKey1);
            }
    
            [Fact]
            public void Then_It_Should_Not_Have_Entries_For_Inexistent_Parameters()
            {
                _result.TryGetValue("key2", out List<string> _).Should().BeFalse();
            }
        }
    
        public class Given_A_Query_String_With_Non_Url_Encoded_Null_Values_When_Creating_From_A_Query
            : Given_When_Then_Test
        {
            private QueryParserFactory _sut;
            private Dictionary<string, List<string>> _result;
            private List<string> _expectedValueForKey1;
            private List<string> _expectedValueForKey2;
    
            protected override void Given()
            {
                _expectedValueForKey1 = new List<string>()
                {
                    "null"
                };
    
                _expectedValueForKey2 = new List<string>()
                {
                    "null"
                };
    
                _sut = new QueryParserFactory();
            }
    
            protected override void When()
            {
                _result = _sut.CreateFromQuery("?key1=null&key2=null");
            }
    
            [Fact]
            public void Then_It_Should_Return_A_Valid_Result()
            {
                _result.Should().NotBeNull();
            }
    
            [Fact]
            public void Then_It_Should_Have_Key_For_All_Fulfilled_Parameters()
            {
                _result.Keys.Count.Should().Be(2);
            }
    
            [Fact]
            public void Then_It_Should_Have_A_Null_Literal_For_The_First_Parameter()
            {
                _result["key1"].Should().BeEquivalentTo(_expectedValueForKey1);
            }
    
            [Fact]
            public void Then_It_Should_Have_A_Null_Literal_For_The_Second_Parameter()
            {
                _result["key2"].Should().BeEquivalentTo(_expectedValueForKey2);
            }
    
            [Fact]
            public void Then_It_Should_Not_Have_Entries_For_Inexistent_Parameters()
            {
                _result.TryGetValue("key3", out List<string> _).Should().BeFalse();
            }
        }
    
        public class Given_A_Query_String_With_Url_Encoded_Null_Values_When_Creating_From_A_Query
            : Given_When_Then_Test
        {
            private QueryParserFactory _sut;
            private Dictionary<string, List<string>> _result;
            private List<string> _expectedValueForKey1;
            private List<string> _expectedValueForKey2;
    
            protected override void Given()
            {
                _expectedValueForKey1 = new List<string>()
                {
                    null
                };
    
                _expectedValueForKey2 = new List<string>()
                {
                    null
                };
    
                _sut = new QueryParserFactory();
            }
    
            protected override void When()
            {
                _result = _sut.CreateFromQuery("?key1=%00&key2=%00");
            }
    
            [Fact]
            public void Then_It_Should_Return_A_Valid_Result()
            {
                _result.Should().NotBeNull();
            }
    
            [Fact]
            public void Then_It_Should_Have_Key_For_All_Fulfilled_Parameters()
            {
                _result.Keys.Count.Should().Be(2);
            }
    
            [Fact]
            public void Then_It_Should_Have_A_Null_Literal_For_The_First_Parameter()
            {
                _result["key1"].Should().BeEquivalentTo(_expectedValueForKey1);
            }
    
            [Fact]
            public void Then_It_Should_Have_A_Null_Literal_For_The_Second_Parameter()
            {
                _result["key2"].Should().BeEquivalentTo(_expectedValueForKey2);
            }
    
            [Fact]
            public void Then_It_Should_Not_Have_Entries_For_Inexistent_Parameters()
            {
                _result.TryGetValue("key3", out List<string> _).Should().BeFalse();
            }
        }
    }
    

    最佳答案

    传递null的方法是根本不传递任何值或完全排除键,即:

    ?key1=foo1&key1=foo2&key2=&key3=
    

    或者简单地:
    ?key1=foo1&key1=foo2&key2=
    

    对于key2参数,您应该注意,无法传递空字符串。 ASP.NET Core会将其解释为空值。如果您具有实际上不应该为null的字符串属性(即,在这种情况下,您希望它始终为空字符串),则可以通过自定义getter进行处理。
    private string key2;
    public string Key2
    {
        get => key2 ?? string.Empty;
        set => key2 = value;
    }
    

    然后,在将其设置为null值的情况下,它将替代为空字符串。

    关于c# - 在AspNet Core中解析空查询参数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51478601/

    10-09 00:34
    查看更多