Reputation: 205
How do I bind a list of Guids passed on the query string for a web-api call.
I've tried comma separated list, an array, etc. but they always come up with an empty guid on the controller action
public endPoint:
public ApiResult Get([FromUri]List<Guid> listIds)
What I've tried
endpoint?listIds=78CC5308-5A3D-40C2-8D7C-3AAB0CAC618B,6DA7AFB8-D862-4861-8EA2-EE5EB9BC7C6A
endpoint?listIds=["78CC5308-5A3D-40C2-8D7C-3AAB0CAC618B","6DA7AFB8-D862-4861-8EA2-EE5EB9BC7C6A"]
All I get each time I try is a list with one element and it is the empty GUID
Upvotes: 4
Views: 8708
Reputation: 12685
You could customize QueryStringValueProvider like below :
SeparatedQueryStringValueProvider
public class SeparatedQueryStringValueProvider : QueryStringValueProvider
{
private readonly string _key;
private readonly string _separator;
private readonly IQueryCollection _values;
public SeparatedQueryStringValueProvider(IQueryCollection values, string separator)
: this(null, values, separator)
{
}
public SeparatedQueryStringValueProvider(string key, IQueryCollection values, string separator)
: base(BindingSource.Query, values, CultureInfo.InvariantCulture)
{
_key = key;
_values = values;
_separator = separator;
}
public override ValueProviderResult GetValue(string key)
{
var result = base.GetValue(key);
if (_key != null && _key != key)
{
return result;
}
if (result != ValueProviderResult.None && result.Values.Any(x => x.IndexOf(_separator, StringComparison.OrdinalIgnoreCase) > 0))
{
var splitValues = new StringValues(result.Values
.SelectMany(x => x.Split(new[] { _separator }, StringSplitOptions.None)).ToArray());
return new ValueProviderResult(splitValues, result.Culture);
}
return result;
}
}
Add an IValueProviderFactory
. Factories are – as the name suggests – responsible for providing instances of value providers. Our factory is very simple :
public class SeparatedQueryStringValueProviderFactory : IValueProviderFactory
{
private readonly string _separator;
private readonly string _key;
public SeparatedQueryStringValueProviderFactory(string separator) : this(null, separator)
{
}
public SeparatedQueryStringValueProviderFactory(string key, string separator)
{
_key = key;
_separator = separator;
}
public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
{
context.ValueProviders.Insert(0, new SeparatedQueryStringValueProvider(_key, context.ActionContext.HttpContext.Request.Query, _separator));
return Task.CompletedTask;
}
}
To apply the value provider globally to our entire API, we can register the factory in the MVC Options, at application startup.
services.AddMvc(opts =>
{
opts.ValueProviderFactories.Insert(0, new SeparatedQueryStringValueProviderFactory(","));
});
Note: the value provider is applied against a method (an action), not against an individual parameter of that action. This means that typically, if you create a custom value provider strategy it would be applied to all parameters of an action. For how to remedy this defect, you can refer to this link : https://www.strathweb.com/2017/07/customizing-query-string-parameter-binding-in-asp-net-core-mvc/
Upvotes: 0
Reputation: 4516
The right way for webapi is using the query parameter multiple times,no matter the type.
i.e:
endpoint?listIds=1234-5678&listIds=5555-4444....
Upvotes: 9
Reputation: 205
The trick is to add an individual entry for each guid in the query string. So the values bind properly when I GET
endpoint?listIds=78CC5308-5A3D-40C2-8D7C-3AAB0CAC618B&listIds=6DA7AFB8-D862-4861-8EA2-EE5EB9BC7C6A
Upvotes: 2