MasterCrafter
MasterCrafter

Reputation: 205

How do I bind list of guids on the query string in C# web api?

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

Answers (3)

Xueli Chen
Xueli Chen

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

Ziv Weissman
Ziv Weissman

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

MasterCrafter
MasterCrafter

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

Related Questions