3

How to pass int[] to HttpGet method in ASP.NET Core? (Not as query parameters!)

Every post I found talks about query params, but query params are not required.

I would like something like that:

[HttpGet("List/{ids}")]
public async Task<ActionResult<ViewItemModel[]>> List(int[] ids)

but ids are empty array. I call controller method with url: http://localh.../List/2062,2063,2064.
Swagger (Swashbuckle) calls method exactly the same.

I found this post but it is 5 years old and not for ASP.NET Core.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
Makla
  • 9,899
  • 16
  • 72
  • 142
  • 1
    Have a look at this and see if it helps. might need to make some minor adjustments as it was for previous version but it works https://stackoverflow.com/a/37768858/5233410 – Nkosi Jul 25 '18 at 10:35
  • In the end you will need a custom model binder. – Nkosi Jul 25 '18 at 10:37
  • Not sure anymore if this is duplicate. Accepted answer in link you provided is perfect and something I am looking for. Problem is it doesn't work in ASP.NET Core. Until that I will use your answer. – Makla Jul 25 '18 at 10:44
  • 1
    Ok no problem. If you get stuck ping me and I'll see where I can help. – Nkosi Jul 25 '18 at 10:45

1 Answers1

3

All the credit goes more or less to Nkosi answer here.

public class EnumerableBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (!typeof(IEnumerable<int>).IsAssignableFrom(bindingContext.ModelType))
            throw new OpPISException("Model is not assignable from IEnumerable<int>.");

        var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (val == null)
            throw new NullReferenceException();

        var ids = val.Values.FirstOrDefault();
        if (ids == null)
            throw new NullReferenceException();

        var tokens = ids.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
        if (tokens.Length > 0)
        {
            try 
            {
                var clientsId = tokens.Select(int.Parse);
                object model = null;
                if (bindingContext.ModelType.IsArray)
                {
                    model = clientsId.ToArray();
                }
                else if (bindingContext.ModelType == typeof(HashSet<int>))
                {
                    model = clientsId.ToHashSet();
                }
                else
                {
                    model = clientsId.ToList();
                }                        
                bindingContext.ModelState.SetModelValue(bindingContext.ModelName, model);
                bindingContext.Result = ModelBindingResult.Success(model);
                return Task.CompletedTask;
            }
            catch {
                //...
            }
        }

        //If we reach this far something went wrong
        bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "Cannot convert.");
        bindingContext.Result = ModelBindingResult.Failed();
        return Task.CompletedTask;
    }
}

Use case:

[HttpGet("List/{ids}")]
public async Task<ActionResult<ViewItemModel[]>> List([ModelBinder(typeof(EnumerableBinder))]HashSet<int> ids)
{
    //code
}

With a little reflection this could be changed to use also other types then int.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
Makla
  • 9,899
  • 16
  • 72
  • 142