diff --git a/Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs b/Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs index cd3102d6..c8d0cc74 100644 --- a/Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs +++ b/Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs @@ -61,6 +61,18 @@ public void can_set_query_params() { Assert.AreEqual("http://www.mysite.com/more?x=1&y=2&y=4&y=6&z=3&abc&xyz&foo=&=bar", url.ToString()); } + [Test] + public void can_append_query_params() { + var url = "http://www.mysite.com/more" + .AppendQueryParam("x", 1) + .AppendQueryParam("x", new[] { "2", "4", "6" }) + .AppendQueryParam("x", 3) + .AppendQueryParam("x") + .AppendQueryParam("x", ""); + + Assert.AreEqual("http://www.mysite.com/more?x=1&x=2&x=4&x=6&x=3&x&x=", url.ToString()); + } + // #641 (oddly, passing the params object ("" or null) via another TestCase arg didn't repro the bug in the null case) [TestCase("http://www.mysite.com/more")] [TestCase("http://www.mysite.com/more?x=1")] @@ -81,6 +93,16 @@ public void can_set_query_params_using_objects_with_nullable_types() { Assert.AreEqual("https://api.com?x=1", url.ToString()); } + [Test] // #669 + public void can_append_query_params_using_objects_with_nullable_types() { + int? x = 1; + int? y = null; + var query = new { x, y }; + var url = new Url("https://api.com"); + url.AppendQueryParam(query); + Assert.AreEqual("https://api.com?x=1", url.ToString()); + } + #if NET [Test] // #632 public void can_set_query_params_to_enums_cast_to_ints() { @@ -104,6 +126,13 @@ public void can_set_query_params_using_object_with_ienumerable() { Assert.AreEqual("https://api.com?Values=1&Values=2&Values=3", url.ToString()); } + [Test] // #672 + public void can_append_query_params_using_object_with_ienumerable() { + var model = new ModelWithIEnumerable { Values = Enumerable.Range(1, 3).ToArray() }; + var url = "https://api.com".AppendQueryParam(model); + Assert.AreEqual("https://api.com?Values=1&Values=2&Values=3", url.ToString()); + } + class ModelWithIEnumerable { public IEnumerable Values { get; set; } diff --git a/src/Flurl.CodeGen/Metadata.cs b/src/Flurl.CodeGen/Metadata.cs index dd94dbf9..d2c0ecaa 100644 --- a/src/Flurl.CodeGen/Metadata.cs +++ b/src/Flurl.CodeGen/Metadata.cs @@ -53,6 +53,31 @@ public static IEnumerable GetUrlReturningExtensions(MethodArg e yield return Create("SetQueryParams", "Creates a new Url object from the string and adds multiple parameters without values to the query.") .AddArg("names", "params string[]", "Names of query parameters"); + yield return Create("AppendQueryParam", "Creates a new Url object from the string and adds a parameter to the query.") + .AddArg("name", "string", "Name of query parameter") + .AddArg("value", "object", "Value of query parameter") + .AddArg("nullValueHandling", "NullValueHandling", "Indicates how to handle null values. Defaults to Remove (any existing)", "NullValueHandling.Remove"); + + yield return Create("AppendQueryParam", "Creates a new Url object from the string and adds a parameter to the query.") + .AddArg("name", "string", "Name of query parameter") + .AddArg("value", "string", "Value of query parameter") + .AddArg("isEncoded", "bool", "Set to true to indicate the value is already URL-encoded. Defaults to false.", "false") + .AddArg("nullValueHandling", "NullValueHandling", "Indicates how to handle null values. Defaults to Remove (any existing).", "NullValueHandling.Remove"); + + yield return Create("AppendQueryParam", "Creates a new Url object from the string and adds a parameter without a value to the query.") + .AddArg("name", "string", "Name of query parameter"); + + yield return Create("AppendQueryParam", "Creates a new Url object from the string, parses values object into name/value pairs, and adds them to the query, overwriting any that already exist.") + .AddArg("values", "object", "Typically an anonymous object, ie: new { x = 1, y = 2 }") + .AddArg("nullValueHandling", "NullValueHandling", "Indicates how to handle null values. Defaults to Remove (any existing)", "NullValueHandling.Remove"); + + yield return Create("AppendQueryParam", "Creates a new Url object from the string and adds multiple parameters without values to the query.") + .AddArg("names", "IEnumerable", "Names of query parameters."); + + yield return Create("AppendQueryParam", "Creates a new Url object from the string and adds multiple parameters without values to the query.") + .AddArg("names", "params string[]", "Names of query parameters"); + + yield return Create("RemoveQueryParam", "Creates a new Url object from the string and removes a name/value pair from the query by name.") .AddArg("name", "string", "Query string parameter name to remove"); @@ -98,7 +123,7 @@ public static IEnumerable GetRequestReturningExtensions(MethodA .AddArg("name", "string", "The cookie name.") .AddArg("value", "object", "The cookie value."); yield return Create("WithCookies", "Creates a new FlurlRequest and adds name-value pairs to its Cookie header based on property names/values of the provided object, or keys/values if object is a dictionary. " + - "To automatically maintain a cookie \"session\", consider using a CookieJar or CookieSession instead.") + "To automatically maintain a cookie \"session\", consider using a CookieJar or CookieSession instead.") .AddArg("values", "object", "Names/values of HTTP cookies to set. Typically an anonymous object or IDictionary."); yield return Create("WithCookies", "Creates a new FlurlRequest and sets the CookieJar associated with this request, which will be updated with any Set-Cookie headers present in the response and is suitable for reuse in subsequent requests.") .AddArg("cookieJar", "CookieJar", "The CookieJar."); diff --git a/src/Flurl.Http/UrlBuilderExtensions.cs b/src/Flurl.Http/UrlBuilderExtensions.cs index 83fce84b..54fbb5da 100644 --- a/src/Flurl.Http/UrlBuilderExtensions.cs +++ b/src/Flurl.Http/UrlBuilderExtensions.cs @@ -116,6 +116,79 @@ public static IFlurlRequest SetQueryParams(this IFlurlRequest request, params st return request; } + /// + /// Adds a parameter to the URL query. + /// + /// The IFlurlRequest associated with the URL + /// Name of query parameter + /// Value of query parameter + /// Indicates how to handle null values. Defaults to Remove (any existing) + /// This IFlurlRequest + public static IFlurlRequest AppendQueryParam(this IFlurlRequest request, string name, object value, NullValueHandling nullValueHandling = NullValueHandling.Remove) { + request.Url.AppendQueryParam(name, value, nullValueHandling); + return request; + } + + /// + /// Adds a parameter to the URL query. + /// + /// The IFlurlRequest associated with the URL + /// Name of query parameter + /// Value of query parameter + /// Set to true to indicate the value is already URL-encoded + /// Indicates how to handle null values. Defaults to Remove (any existing) + /// This IFlurlRequest + /// is . + public static IFlurlRequest AppendQueryParam(this IFlurlRequest request, string name, string value, bool isEncoded = false, NullValueHandling nullValueHandling = NullValueHandling.Remove) { + request.Url.AppendQueryParam(name, value, isEncoded, nullValueHandling); + return request; + } + + /// + /// Adds a parameter without a value to the URL query. + /// + /// The IFlurlRequest associated with the URL + /// Name of query parameter + /// This IFlurlRequest + public static IFlurlRequest AppendQueryParam(this IFlurlRequest request, string name) { + request.Url.AppendQueryParam(name); + return request; + } + + /// + /// Parses values (usually an anonymous object or dictionary) into name/value pairs and adds them to the URL query, overwriting any that already exist. + /// + /// The IFlurlRequest associated with the URL + /// Typically an anonymous object, ie: new { x = 1, y = 2 } + /// Indicates how to handle null values. Defaults to Remove (any existing) + /// This IFlurlRequest + public static IFlurlRequest AppendQueryParam(this IFlurlRequest request, object values, NullValueHandling nullValueHandling = NullValueHandling.Remove) { + request.Url.AppendQueryParam(values, nullValueHandling); + return request; + } + + /// + /// Adds multiple parameters without values to the URL query. + /// + /// The IFlurlRequest associated with the URL + /// Names of query parameters. + /// This IFlurlRequest + public static IFlurlRequest AppendQueryParam(this IFlurlRequest request, IEnumerable names) { + request.Url.AppendQueryParam(names); + return request; + } + + /// + /// Adds multiple parameters without values to the URL query. + /// + /// The IFlurlRequest associated with the URL + /// Names of query parameters + /// This IFlurlRequest + public static IFlurlRequest AppendQueryParam(this IFlurlRequest request, params string[] names) { + request.Url.AppendQueryParam(names as IEnumerable); + return request; + } + /// /// Removes a name/value pair from the URL query by name. /// diff --git a/src/Flurl/Url.cs b/src/Flurl/Url.cs index 1bc25e92..af1a3518 100644 --- a/src/Flurl/Url.cs +++ b/src/Flurl/Url.cs @@ -400,6 +400,83 @@ public Url SetQueryParams(IEnumerable names) { /// The Url object with the query parameter added. public Url SetQueryParams(params string[] names) => SetQueryParams(names as IEnumerable); + /// + /// Adds a parameter to the query. + /// + /// Name of query parameter + /// Value of query parameter + /// Indicates how to handle null values. Defaults to Remove (any existing) + /// The Url object with the query parameter added + public Url AppendQueryParam(string name, object value, NullValueHandling nullValueHandling = NullValueHandling.Remove) { + QueryParams.Add(name, value, false, nullValueHandling); + return this; + } + + /// + /// Adds a parameter to the query. + /// + /// Name of query parameter + /// Value of query parameter + /// Set to true to indicate the value is already URL-encoded + /// Indicates how to handle null values. Defaults to Remove (any existing) + /// The Url object with the query parameter added + /// is . + public Url AppendQueryParam(string name, string value, bool isEncoded = false, NullValueHandling nullValueHandling = NullValueHandling.Remove) { + QueryParams.Add(name, value, isEncoded, nullValueHandling); + return this; + } + + /// + /// Adds a parameter without a value to the query. + /// + /// Name of query parameter + /// The Url object with the query parameter added + public Url AppendQueryParam(string name) { + QueryParams.Add(name, null, false, NullValueHandling.NameOnly); + return this; + } + + /// + /// Parses values (usually an anonymous object or dictionary) into name/value pairs and adds them to the query. + /// + /// Typically an anonymous object, ie: new { x = 1, y = 2 } + /// Indicates how to handle null values. Defaults to Remove (any existing) + /// The Url object with the query parameters added + public Url AppendQueryParam(object values, NullValueHandling nullValueHandling = NullValueHandling.Remove) { + if (values == null) + return this; + + if (values is string s) + return AppendQueryParam(s); + + foreach (var kv in values.ToKeyValuePairs()) + AppendQueryParam(kv.Key, kv.Value, nullValueHandling); + + return this; + } + + /// + /// Adds multiple parameters without values to the query. + /// + /// Names of query parameters. + /// The Url object with the query parameter added + public Url AppendQueryParam(IEnumerable names) { + if (names == null) + return this; + + foreach (var name in names.Where(n => !string.IsNullOrEmpty(n))) + AppendQueryParam(name); + + return this; + } + + /// + /// Adds multiple parameters without values to the query. + /// + /// Names of query parameters + /// The Url object with the query parameter added. + public Url AppendQueryParam(params string[] names) => SetQueryParams(names as IEnumerable); + /// /// Removes a name/value pair from the query by name. ///