diff --git a/src/Http/Http.Extensions/ref/Microsoft.AspNetCore.Http.Extensions.netcoreapp.cs b/src/Http/Http.Extensions/ref/Microsoft.AspNetCore.Http.Extensions.netcoreapp.cs index 14f3400e95..7fa291f591 100644 --- a/src/Http/Http.Extensions/ref/Microsoft.AspNetCore.Http.Extensions.netcoreapp.cs +++ b/src/Http/Http.Extensions/ref/Microsoft.AspNetCore.Http.Extensions.netcoreapp.cs @@ -43,6 +43,7 @@ namespace Microsoft.AspNetCore.Http.Extensions public partial class QueryBuilder : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { public QueryBuilder() { } + public QueryBuilder(System.Collections.Generic.IEnumerable> parameters) { } public QueryBuilder(System.Collections.Generic.IEnumerable> parameters) { } public void Add(string key, System.Collections.Generic.IEnumerable values) { } public void Add(string key, string value) { } diff --git a/src/Http/Http.Extensions/src/QueryBuilder.cs b/src/Http/Http.Extensions/src/QueryBuilder.cs index e9feb391b1..ab2d95b79d 100644 --- a/src/Http/Http.Extensions/src/QueryBuilder.cs +++ b/src/Http/Http.Extensions/src/QueryBuilder.cs @@ -3,8 +3,10 @@ using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Text.Encodings.Web; +using Microsoft.Extensions.Primitives; namespace Microsoft.AspNetCore.Http.Extensions { @@ -23,6 +25,12 @@ namespace Microsoft.AspNetCore.Http.Extensions _params = new List>(parameters); } + public QueryBuilder(IEnumerable> parameters) + : this(parameters.SelectMany(kvp => kvp.Value, (kvp, v) => KeyValuePair.Create(kvp.Key, v))) + { + + } + public void Add(string key, IEnumerable values) { foreach (var value in values) @@ -78,4 +86,4 @@ namespace Microsoft.AspNetCore.Http.Extensions return _params.GetEnumerator(); } } -} \ No newline at end of file +} diff --git a/src/Http/Http.Extensions/test/QueryBuilderTests.cs b/src/Http/Http.Extensions/test/QueryBuilderTests.cs index 7d15dd87bf..c2517c45d4 100644 --- a/src/Http/Http.Extensions/test/QueryBuilderTests.cs +++ b/src/Http/Http.Extensions/test/QueryBuilderTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using Microsoft.Extensions.Primitives; using Xunit; namespace Microsoft.AspNetCore.Http.Extensions @@ -70,6 +71,18 @@ namespace Microsoft.AspNetCore.Http.Extensions Assert.Equal("?key1=value1&key2=value2&key3=value3", builder.ToString()); } + [Fact] + public void AddMultipleValuesViaConstructor_WithStringValues() + { + var builder = new QueryBuilder(new[] + { + new KeyValuePair("key1", new StringValues(new [] { "value1", string.Empty, "value3" })), + new KeyValuePair("key2", string.Empty), + new KeyValuePair("key3", StringValues.Empty) + }); + Assert.Equal("?key1=value1&key1=&key1=value3&key2=", builder.ToString()); + } + [Fact] public void AddMultipleValuesViaInitializer_AddedInOrder() { @@ -95,4 +108,4 @@ namespace Microsoft.AspNetCore.Http.Extensions Assert.Equal("?key1=value1&key2=value2&key3=value3", builder1.ToString()); } } -} \ No newline at end of file +} diff --git a/src/Http/WebUtilities/ref/Microsoft.AspNetCore.WebUtilities.netcoreapp.cs b/src/Http/WebUtilities/ref/Microsoft.AspNetCore.WebUtilities.netcoreapp.cs index 886f5c0052..3aa6ce55dc 100644 --- a/src/Http/WebUtilities/ref/Microsoft.AspNetCore.WebUtilities.netcoreapp.cs +++ b/src/Http/WebUtilities/ref/Microsoft.AspNetCore.WebUtilities.netcoreapp.cs @@ -216,6 +216,8 @@ namespace Microsoft.AspNetCore.WebUtilities public static partial class QueryHelpers { public static string AddQueryString(string uri, System.Collections.Generic.IDictionary queryString) { throw null; } + public static string AddQueryString(string uri, System.Collections.Generic.IEnumerable> queryString) { throw null; } + public static string AddQueryString(string uri, System.Collections.Generic.IEnumerable> queryString) { throw null; } public static string AddQueryString(string uri, string name, string value) { throw null; } public static System.Collections.Generic.Dictionary ParseNullableQuery(string queryString) { throw null; } public static System.Collections.Generic.Dictionary ParseQuery(string queryString) { throw null; } diff --git a/src/Http/WebUtilities/src/QueryHelpers.cs b/src/Http/WebUtilities/src/QueryHelpers.cs index ca71329f03..9c28f4ab2b 100644 --- a/src/Http/WebUtilities/src/QueryHelpers.cs +++ b/src/Http/WebUtilities/src/QueryHelpers.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Text.Encodings.Web; using Microsoft.Extensions.Primitives; @@ -46,10 +47,10 @@ namespace Microsoft.AspNetCore.WebUtilities } /// - /// Append the given query keys and values to the uri. + /// Append the given query keys and values to the URI. /// - /// The base uri. - /// A collection of name value query pairs to append. + /// The base URI. + /// A dictionary of query keys and values to append. /// The combined result. /// is null. /// is null. @@ -68,7 +69,38 @@ namespace Microsoft.AspNetCore.WebUtilities return AddQueryString(uri, (IEnumerable>)queryString); } - private static string AddQueryString( + /// + /// Append the given query keys and values to the URI. + /// + /// The base URI. + /// A collection of query names and values to append. + /// The combined result. + /// is null. + /// is null. + public static string AddQueryString(string uri, IEnumerable> queryString) + { + if (uri == null) + { + throw new ArgumentNullException(nameof(uri)); + } + + if (queryString == null) + { + throw new ArgumentNullException(nameof(queryString)); + } + + return AddQueryString(uri, queryString.SelectMany(kvp => kvp.Value, (kvp, v) => KeyValuePair.Create(kvp.Key, v))); + } + + /// + /// Append the given query keys and values to the URI. + /// + /// The base URI. + /// A collection of name value query pairs to append. + /// The combined result. + /// is null. + /// is null. + public static string AddQueryString( string uri, IEnumerable> queryString) { diff --git a/src/Http/WebUtilities/test/QueryHelpersTests.cs b/src/Http/WebUtilities/test/QueryHelpersTests.cs index a64bcbf03b..204813e5b6 100644 --- a/src/Http/WebUtilities/test/QueryHelpersTests.cs +++ b/src/Http/WebUtilities/test/QueryHelpersTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Primitives; using Xunit; namespace Microsoft.AspNetCore.WebUtilities @@ -119,5 +120,37 @@ namespace Microsoft.AspNetCore.WebUtilities var result = QueryHelpers.AddQueryString(uri, queryStrings); Assert.Equal(expectedUri, result); } + + [Theory] + [InlineData("http://contoso.com/", "http://contoso.com/?param1=value1¶m1=¶m1=value3¶m2=")] + [InlineData("http://contoso.com/someaction", "http://contoso.com/someaction?param1=value1¶m1=¶m1=value3¶m2=")] + [InlineData("http://contoso.com/someaction?param2=1", "http://contoso.com/someaction?param2=1¶m1=value1¶m1=¶m1=value3¶m2=")] + [InlineData("http://contoso.com/some#action", "http://contoso.com/some?param1=value1¶m1=¶m1=value3¶m2=#action")] + [InlineData("http://contoso.com/some?param2=1#action", "http://contoso.com/some?param2=1¶m1=value1¶m1=¶m1=value3¶m2=#action")] + [InlineData("http://contoso.com/#action", "http://contoso.com/?param1=value1¶m1=¶m1=value3¶m2=#action")] + [InlineData( + "http://contoso.com/someaction?q=test#anchor?value", + "http://contoso.com/someaction?q=test¶m1=value1¶m1=¶m1=value3¶m2=#anchor?value")] + [InlineData( + "http://contoso.com/someaction#anchor?stuff", + "http://contoso.com/someaction?param1=value1¶m1=¶m1=value3¶m2=#anchor?stuff")] + [InlineData( + "http://contoso.com/someaction?name?something", + "http://contoso.com/someaction?name?something¶m1=value1¶m1=¶m1=value3¶m2=")] + [InlineData( + "http://contoso.com/someaction#name#something", + "http://contoso.com/someaction?param1=value1¶m1=¶m1=value3¶m2=#name#something")] + public void AddQueryStringWithEnumerableOfKeysAndStringValues(string uri, string expectedUri) + { + var queryStrings = new Dictionary() + { + { "param1", new StringValues(new [] { "value1", string.Empty, "value3" }) }, + { "param2", string.Empty }, + { "param3", StringValues.Empty } + }; + + var result = QueryHelpers.AddQueryString(uri, queryStrings); + Assert.Equal(expectedUri, result); + } } }