diff --git a/src/Http/Http/src/Features/QueryFeature.cs b/src/Http/Http/src/Features/QueryFeature.cs index bc7b1c7d2cf2..dd3a440202e8 100644 --- a/src/Http/Http/src/Features/QueryFeature.cs +++ b/src/Http/Http/src/Features/QueryFeature.cs @@ -143,7 +143,9 @@ public IQueryCollection Query { if (!querySegment.IsEmpty) { - accumulator.Append(querySegment); + var name = SpanHelper.ReplacePlusWithSpace(querySegment); + + accumulator.Append(Uri.UnescapeDataString(name)); } } diff --git a/src/Http/Http/test/Features/QueryFeatureTests.cs b/src/Http/Http/test/Features/QueryFeatureTests.cs index 04a0a7c8c462..321dc8bda3cd 100644 --- a/src/Http/Http/test/Features/QueryFeatureTests.cs +++ b/src/Http/Http/test/Features/QueryFeatureTests.cs @@ -150,5 +150,75 @@ public void ParseEmptyOrNullQueryWorks(string queryString) Assert.Empty(queryCollection); } + + [Fact] + public void ParseQueryWithEncodedKeyWorks() + { + var features = new FeatureCollection(); + features[typeof(IHttpRequestFeature)] = new HttpRequestFeature { QueryString = "?fields+%5BtodoItems%5D" }; + + var provider = new QueryFeature(features); + + var queryCollection = provider.Query; + + Assert.Single(queryCollection); + Assert.Equal("", queryCollection["fields [todoItems]"].FirstOrDefault()); + } + + [Fact] + public void ParseQueryWithEncodedValueWorks() + { + var features = new FeatureCollection(); + features[typeof(IHttpRequestFeature)] = new HttpRequestFeature { QueryString = "?=fields+%5BtodoItems%5D" }; + + var provider = new QueryFeature(features); + + var queryCollection = provider.Query; + + Assert.Single(queryCollection); + Assert.Equal("fields [todoItems]", queryCollection[""].FirstOrDefault()); + } + + [Fact] + public void ParseQueryWithEncodedKeyEmptyValueWorks() + { + var features = new FeatureCollection(); + features[typeof(IHttpRequestFeature)] = new HttpRequestFeature { QueryString = "?fields+%5BtodoItems%5D=" }; + + var provider = new QueryFeature(features); + + var queryCollection = provider.Query; + + Assert.Single(queryCollection); + Assert.Equal("", queryCollection["fields [todoItems]"].FirstOrDefault()); + } + + [Fact] + public void ParseQueryWithEncodedKeyEncodedValueWorks() + { + var features = new FeatureCollection(); + features[typeof(IHttpRequestFeature)] = new HttpRequestFeature { QueryString = "?fields+%5BtodoItems%5D=%5B+1+%5D" }; + + var provider = new QueryFeature(features); + + var queryCollection = provider.Query; + + Assert.Single(queryCollection); + Assert.Equal("[ 1 ]", queryCollection["fields [todoItems]"].FirstOrDefault()); + } + + [Fact] + public void ParseQueryWithEncodedKeyEncodedValuesWorks() + { + var features = new FeatureCollection(); + features[typeof(IHttpRequestFeature)] = new HttpRequestFeature { QueryString = "?fields+%5BtodoItems%5D=%5B+1+%5D&fields+%5BtodoItems%5D=%5B+2+%5D" }; + + var provider = new QueryFeature(features); + + var queryCollection = provider.Query; + + Assert.Single(queryCollection); + Assert.Equal(new[] { "[ 1 ]", "[ 2 ]" }, queryCollection["fields [todoItems]"]); + } } } diff --git a/src/Http/WebUtilities/src/QueryHelpers.cs b/src/Http/WebUtilities/src/QueryHelpers.cs index 137035b34076..64a2ba2db6ba 100644 --- a/src/Http/WebUtilities/src/QueryHelpers.cs +++ b/src/Http/WebUtilities/src/QueryHelpers.cs @@ -218,7 +218,10 @@ public static Dictionary ParseQuery(string? queryString) { if (delimiterIndex > scanIndex) { - accumulator.Append(queryString.Substring(scanIndex, delimiterIndex - scanIndex), string.Empty); + string name = queryString.Substring(scanIndex, delimiterIndex - scanIndex); + accumulator.Append( + Uri.UnescapeDataString(name.Replace('+', ' ')), + string.Empty); } } scanIndex = delimiterIndex + 1; diff --git a/src/Http/WebUtilities/test/QueryHelpersTests.cs b/src/Http/WebUtilities/test/QueryHelpersTests.cs index 1c9ed8666cba..9e50589d20b3 100644 --- a/src/Http/WebUtilities/test/QueryHelpersTests.cs +++ b/src/Http/WebUtilities/test/QueryHelpersTests.cs @@ -55,6 +55,46 @@ public void ParseQueryWithEmptyKeyWorks() Assert.Equal(new[] { "value1", "" }, collection[""]); } + [Fact] + public void ParseQueryWithEncodedKeyWorks() + { + var collection = QueryHelpers.ParseQuery("?fields+%5BtodoItems%5D"); + Assert.Single(collection); + Assert.Equal("", collection["fields [todoItems]"].FirstOrDefault()); + } + + [Fact] + public void ParseQueryWithEncodedValueWorks() + { + var collection = QueryHelpers.ParseQuery("?=fields+%5BtodoItems%5D"); + Assert.Single(collection); + Assert.Equal("fields [todoItems]", collection[""].FirstOrDefault()); + } + + [Fact] + public void ParseQueryWithEncodedKeyEmptyValueWorks() + { + var collection = QueryHelpers.ParseQuery("?fields+%5BtodoItems%5D="); + Assert.Single(collection); + Assert.Equal("", collection["fields [todoItems]"].FirstOrDefault()); + } + + [Fact] + public void ParseQueryWithEncodedKeyEncodedValueWorks() + { + var collection = QueryHelpers.ParseQuery("?fields+%5BtodoItems%5D=%5B+1+%5D"); + Assert.Single(collection); + Assert.Equal("[ 1 ]", collection["fields [todoItems]"].FirstOrDefault()); + } + + [Fact] + public void ParseQueryWithEncodedKeyEncodedValuesWorks() + { + var collection = QueryHelpers.ParseQuery("?fields+%5BtodoItems%5D=%5B+1+%5D&fields+%5BtodoItems%5D=%5B+2+%5D"); + Assert.Single(collection); + Assert.Equal(new[] { "[ 1 ]", "[ 2 ]" }, collection["fields [todoItems]"]); + } + [Theory] [InlineData("?")] [InlineData("")]