97

I have an ASP.net MVC controller called Designs that has an action with the following signature:

public ActionResult Multiple(int[] ids)

However, when I try to navigate to this action using the url:

http://localhost:54119/Designs/Multiple?ids=24041,24117

The ids parameter is always null. Is there any way to get MVC to convert the ?ids= URL query parameter into an array for the action? I've seen talk of using an action filter but as far as I can tell that will only work for POSTs where the array is passed in the request data rather than in the URL itself.

5 Answers 5

163

The default model binder expects this url:

http://localhost:54119/Designs/Multiple?ids=24041&ids=24117

in order to successfully bind to:

public ActionResult Multiple(int[] ids)
{
    ...
}

And if you want this to work with comma separated values you could write a custom model binder:

public class IntArrayModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (value == null || string.IsNullOrEmpty(value.AttemptedValue))
        {
            return null;
        }

        return value
            .AttemptedValue
            .Split(',')
            .Select(int.Parse)
            .ToArray();
    }
}

and then you could apply this model binder to a particular action argument:

public ActionResult Multiple([ModelBinder(typeof(IntArrayModelBinder))] int[] ids)
{
    ...
}

or apply it globally to all integer array parameters in your Application_Start in Global.asax:

ModelBinders.Binders.Add(typeof(int[]), new IntArrayModelBinder());

and now your controller action might look like this:

public ActionResult Multiple(int[] ids)
{
    ...
}
Sign up to request clarification or add additional context in comments.

2 Comments

I was missing [FromUri]. public ActionResult Multiple([FromUri]int[] ids) {} (GET)
@Darin is there a way to apply the custom binding globally but just ignore for specific action? I couldn't find a way to do that :stackoverflow.com/questions/45379040/…
18

To extend on Darin Dimitrov's answer, something you can get away with is accepting a simple string in your URL parameter and converting it to an array yourself:

public ActionResult Multiple(string ids){
  int[] idsArray = ids.Split(',').Select(int.Parse).ToArray();
  /* ...process results... */
}

If you get a parse error while doing this (because someone passed you a malformed array), you can cause your exception handler to return a 400 Bad Request error instead of the default, more unfriendly 404 Not Found error that MVC returns when an endpoint is not found.

Comments

13

You can also use this URL format, and ASP.NET MVC will do everything for you. But, remember to apply URL encoding.

?param1[0]=3344&param1[1]=2222

1 Comment

This way one can set to null too. ?param1[0]=&param1[1]=2222
10

.Net Core Answer

For those coming here in recent times, you can do this in .Net Core with:

http://localhost:54119/Designs/Multiple?ids=24041&ids=24117

and:

public ActionResult Multiple([FromQuery] int[] ids)
{
    ...
}

Comments

7

I don't know where Groky's URL string was coming from, but I had the same problem with some javascript calling my controller/action. It would build up a URL of null, 1, or many "IDs" from a multiple-select list (which is unique to the solution I'm going to share).

I copy/pasted Darin's custom model binder and decorated my action/parameter, but it didn't work. I still got null valued int[] ids. Even in the "safe" case where I actually did have many IDs.

I ended up changing the javascript to produce an ASP.NET MVC friendly parameter array like

?ids=1&ids=2

I had to do some silly stuff, though

ids || []                 #=> if null, get an empty array
[ids || []]               #=> if a single item, wrap it in an array
[].concat.apply([], ...)  #=> in case I wrapped an array, flatten it

So, the full block was

ids = [].concat.apply([], [ids || []])
id_parameter = 'ids=' + ids.join('&ids=')

It's messy, but it's the first time I had to hack like this in javascript.

2 Comments

Just curious if there's a missing 'not' before "first time" in that last sentence. Otherwise, lucky you!
@DCShannon: haha, I see your point! But, this was my first time. I'm not sure if this kind of thing is normal or if I'm going overboard.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.