Search code examples
c#asp.net-coreasp.net-core-webapi

How to pass int array (int[]) to a Get method in ASP.NET Core


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.


Solution

  • 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.