This commit is contained in:
parent
438b27a6e9
commit
e8d18a09d1
@ -109,9 +109,15 @@ namespace Flurl.Test.Http
|
||||
HttpTest.ShouldHaveMadeACall().WithoutQueryParamValues(new { z = 333, y = 222 }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task can_assert_multiple_occurances_of_query_param() {
|
||||
await "http://www.api.com?x=1&x=2&x=3&y=4#abcd".GetAsync();
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public async Task can_assert_multiple_occurances_of_query_param(bool buildFluently) {
|
||||
// #276 showed that this failed when the URL was built fluently (caused by #301)
|
||||
var url = buildFluently ?
|
||||
"http://www.api.com".SetQueryParam("x", new[] { 1, 2, 3 }).SetQueryParam("y", 4).SetFragment("abcd") :
|
||||
new Url("http://www.api.com?x=1&x=2&x=3&y=4#abcd");
|
||||
|
||||
await url.GetAsync();
|
||||
|
||||
HttpTest.ShouldHaveMadeACall().WithQueryParam("x");
|
||||
HttpTest.ShouldHaveMadeACall().WithQueryParamValue("x", new[] { 2, 1 }); // order shouldn't matter
|
||||
|
@ -82,6 +82,13 @@ namespace Flurl.Test
|
||||
Assert.AreEqual("http://www.mysite.com/more?y=2&z=4", url.ToString());
|
||||
}
|
||||
|
||||
[Test] // #301
|
||||
public void setting_query_param_array_creates_multiple() {
|
||||
var q = "http://www.mysite.com".SetQueryParam("x", new[] { 1, 2, 3 }).QueryParams;
|
||||
Assert.AreEqual(3, q.Count);
|
||||
Assert.AreEqual(new[] { 1, 2, 3 }, q.Select(p => p.Value));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void can_change_query_param() {
|
||||
var url = "http://www.mysite.com?x=1".SetQueryParam("x", 2);
|
||||
|
@ -20,12 +20,9 @@ namespace Flurl.Http.Configuration
|
||||
|
||||
var qp = new QueryParamCollection();
|
||||
foreach (var kv in obj.ToKeyValuePairs())
|
||||
{
|
||||
// if value is null, the serializer shouldn't add this key-value pair.
|
||||
if (kv.Value == null) continue;
|
||||
qp[kv.Key] = new QueryParameter(kv.Key, kv.Value);
|
||||
}
|
||||
return qp.ToString(true);
|
||||
qp.Merge(kv.Key, kv.Value, false, NullValueHandling.Ignore);
|
||||
|
||||
return qp.ToString(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -107,8 +107,8 @@ namespace Flurl.Http.Testing
|
||||
/// <param name="value">The query parameter value. Can contain * wildcard.</param>
|
||||
/// <returns></returns>
|
||||
public HttpCallAssertion WithQueryParamValue(string name, object value) {
|
||||
if (value is IEnumerable && !(value is string)) {
|
||||
foreach (var val in (IEnumerable)value)
|
||||
if (!(value is string) && value is IEnumerable en) {
|
||||
foreach (var val in en)
|
||||
WithQueryParamValue(name, val);
|
||||
return this;
|
||||
}
|
||||
@ -123,8 +123,8 @@ namespace Flurl.Http.Testing
|
||||
/// <param name="value">The query parameter value. Can contain * wildcard.</param>
|
||||
/// <returns></returns>
|
||||
public HttpCallAssertion WithoutQueryParamValue(string name, object value) {
|
||||
if (value is IEnumerable && !(value is string)) {
|
||||
foreach (var val in (IEnumerable)value)
|
||||
if (!(value is string) && value is IEnumerable en) {
|
||||
foreach (var val in en)
|
||||
WithoutQueryParamValue(name, val);
|
||||
return this;
|
||||
}
|
||||
@ -153,8 +153,8 @@ namespace Flurl.Http.Testing
|
||||
private bool QueryParamMatches(QueryParameter qp, string name, object value) {
|
||||
if (qp.Name != name)
|
||||
return false;
|
||||
if (value is string)
|
||||
return MatchesPattern(qp.Value?.ToString(), value?.ToString());
|
||||
if (value is string s)
|
||||
return MatchesPattern(qp.Value?.ToString(), s);
|
||||
return qp.Value?.ToString() == value?.ToString();
|
||||
}
|
||||
|
||||
|
@ -56,6 +56,44 @@ namespace Flurl
|
||||
return RemoveAll(p => p.Name == name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces an existing QueryParameter or appends one to the end. If object is a collection type (array, IEnumerable, etc.),
|
||||
/// multiple paramters are added, i.e. x=1&x=2. If any of the same name already exist, they are overwritten one by one
|
||||
/// (preserving order) and any remaining are appended to the end. If fewer values are specified than already exist,
|
||||
/// remaining existing values are removed.
|
||||
/// </summary>
|
||||
public void Merge(string name, object value, bool isEncoded, NullValueHandling nullValueHandling) {
|
||||
if (value == null && nullValueHandling != NullValueHandling.NameOnly) {
|
||||
if (nullValueHandling == NullValueHandling.Remove)
|
||||
Remove(name);
|
||||
return;
|
||||
}
|
||||
|
||||
// This covers some complex edge cases involving multiple values of the same name.
|
||||
// example: x has values at positions 2 and 4 in the query string, then we set x to
|
||||
// an array of 4 values. We want to replace the values at positions 2 and 4 with the
|
||||
// first 2 values of the new array, then append the remaining 2 values to the end.
|
||||
var parameters = this.Where(p => p.Name == name).ToArray();
|
||||
var values = (!(value is string) && value is IEnumerable en) ? en.Cast<object>().ToArray() : new[] { value };
|
||||
|
||||
for (int i = 0;; i++) {
|
||||
if (i < parameters.Length && i < values.Length) {
|
||||
if (values[i] is QueryParameter qp)
|
||||
this[IndexOf(parameters[i])] = qp;
|
||||
else
|
||||
parameters[i].Value = values[i];
|
||||
}
|
||||
else if (i < parameters.Length)
|
||||
Remove(parameters[i]);
|
||||
else if (i < values.Length) {
|
||||
var qp = values[i] as QueryParameter ?? new QueryParameter(name, values[i], isEncoded);
|
||||
Add(qp);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a query parameter value by name. A query may contain multiple values of the same name
|
||||
/// (i.e. "x=1&x=2"), in which case the value is an array, which works for both getting and setting.
|
||||
@ -71,37 +109,7 @@ namespace Flurl
|
||||
return all[0];
|
||||
return all;
|
||||
}
|
||||
set {
|
||||
// This covers some complex edge cases involving multiple values of the same name.
|
||||
// example: x has values at positions 2 and 4 in the query string, then we set x to
|
||||
// an array of 4 values. We want to replace the values at positions 2 and 4 with the
|
||||
// first 2 values of the new array, then append the remaining 2 values to the end.
|
||||
var parameters = this.Where(p => p.Name == name).ToArray();
|
||||
var values = (value is IEnumerable && !(value is string)) ?
|
||||
(value as IEnumerable).Cast<object>().ToArray() :
|
||||
new[] { value };
|
||||
|
||||
for (int i = 0;; i++) {
|
||||
if (i < parameters.Length && i < values.Length) {
|
||||
if (values[i] == null)
|
||||
Remove(parameters[i]);
|
||||
else if (values[i] is QueryParameter)
|
||||
this[IndexOf(parameters[i])] = (QueryParameter)values[i];
|
||||
else
|
||||
parameters[i].Value = values[i];
|
||||
}
|
||||
else if (i < parameters.Length)
|
||||
Remove(parameters[i]);
|
||||
else if (i < values.Length) {
|
||||
if (values[i] is QueryParameter)
|
||||
Add((QueryParameter)values[i]);
|
||||
else
|
||||
Add(name, values[i]);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
set => Merge(name, value, false, NullValueHandling.Remove);
|
||||
}
|
||||
}
|
||||
}
|
@ -52,21 +52,13 @@ namespace Flurl
|
||||
/// <param name="encodeSpaceAsPlus">Indicates whether to encode space characters with "+" instead of "%20".</param>
|
||||
/// <returns></returns>
|
||||
public string ToString(bool encodeSpaceAsPlus) {
|
||||
if (_value is IEnumerable && !(_value is string)) {
|
||||
return string.Join("&",
|
||||
from v in (_value as IEnumerable).Cast<object>()
|
||||
select BuildPair(Name, v, false, encodeSpaceAsPlus));
|
||||
}
|
||||
return BuildPair(Name, _encodedValue ?? Value, _encodedValue != null, encodeSpaceAsPlus);
|
||||
}
|
||||
var name = Url.EncodeIllegalCharacters(Name, encodeSpaceAsPlus);
|
||||
var value =
|
||||
(_encodedValue != null) ? _encodedValue :
|
||||
(Value != null) ? Url.Encode(Value.ToInvariantString(), encodeSpaceAsPlus) :
|
||||
null;
|
||||
|
||||
private static string BuildPair(string name, object value, bool valueIsEncoded, bool encodeSpaceAsPlus) {
|
||||
name = Url.EncodeIllegalCharacters(name, encodeSpaceAsPlus);
|
||||
if (value == null)
|
||||
return name;
|
||||
|
||||
value = valueIsEncoded ? value : Url.Encode(value.ToInvariantString(), encodeSpaceAsPlus);
|
||||
return $"{name}={value}";
|
||||
return (value == null) ? name : $"{name}={value}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,7 +245,7 @@ namespace Flurl
|
||||
/// <param name="nullValueHandling">Indicates how to handle null values. Defaults to Remove (any existing)</param>
|
||||
/// <returns>The Url object with the query parameter added</returns>
|
||||
public Url SetQueryParam(string name, object value, NullValueHandling nullValueHandling = NullValueHandling.Remove) {
|
||||
SetQueryParamInternal(name, value, false, nullValueHandling);
|
||||
QueryParams.Merge(name, value, false, nullValueHandling);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -259,7 +259,7 @@ namespace Flurl
|
||||
/// <returns>The Url object with the query parameter added</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="name"/> is <see langword="null" />.</exception>
|
||||
public Url SetQueryParam(string name, string value, bool isEncoded = false, NullValueHandling nullValueHandling = NullValueHandling.Remove) {
|
||||
SetQueryParamInternal(name, value, isEncoded, nullValueHandling);
|
||||
QueryParams.Merge(name, value, isEncoded, nullValueHandling);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -269,16 +269,10 @@ namespace Flurl
|
||||
/// <param name="name">Name of query parameter</param>
|
||||
/// <returns>The Url object with the query parameter added</returns>
|
||||
public Url SetQueryParam(string name) {
|
||||
SetQueryParamInternal(name, null, false, NullValueHandling.NameOnly);
|
||||
QueryParams.Merge(name, null, false, NullValueHandling.NameOnly);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void SetQueryParamInternal(string name, object value, bool isEncoded, NullValueHandling nullValueHandling) {
|
||||
if (value != null || nullValueHandling == NullValueHandling.NameOnly)
|
||||
QueryParams[name] = new QueryParameter(name, value, isEncoded);
|
||||
else if (nullValueHandling == NullValueHandling.Remove)
|
||||
RemoveQueryParam(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses values (usually an anonymous object or dictionary) into name/value pairs and adds them to the query, overwriting any that already exist.
|
||||
@ -291,7 +285,7 @@ namespace Flurl
|
||||
return this;
|
||||
|
||||
foreach (var kv in values.ToKeyValuePairs())
|
||||
SetQueryParamInternal(kv.Key, kv.Value, false, nullValueHandling);
|
||||
QueryParams.Merge(kv.Key, kv.Value, false, nullValueHandling);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user