Как принять массив в качестве параметра действия контроллера ASP.NET MVC?

86

У меня есть контроллер ASP.net MVC, Designsкоторый имеет действие со следующей подписью:

public ActionResult Multiple(int[] ids)

Однако, когда я пытаюсь перейти к этому действию, используя URL-адрес:

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

idsПараметр всегда нулевой. Есть ли способ заставить MVC преобразовать ?ids=параметр запроса URL в массив для действия? Я видел разговоры об использовании фильтра действий, но, насколько я могу судить, это будет работать только для POST, где массив передается в данных запроса, а не в самом URL.

Grokys
источник

Ответы:

153

Связыватель модели по умолчанию ожидает этот URL:

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

чтобы успешно выполнить привязку к:

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

И если вы хотите, чтобы это работало со значениями, разделенными запятыми, вы можете написать собственный связыватель модели:

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();
    }
}

а затем вы можете применить эту привязку модели к определенному аргументу действия:

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

или примените его глобально ко всем параметрам целочисленного массива в вашем Application_Startin Global.asax:

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

и теперь действие вашего контроллера может выглядеть так:

public ActionResult Multiple(int[] ids)
{
    ...
}
Дарин Димитров
источник
2
Я пропал [FromUri]. public ActionResult Multiple([FromUri]int[] ids) {} (GET)
C0d1ngJammer
1
@Darin есть ли способ применить настраиваемую привязку глобально, но просто игнорировать для определенного действия? Я не мог найти способ сделать это: stackoverflow.com/questions/45379040/...
ДАК
15

Чтобы продолжить ответ Дарина Димитрова , вам может сойти с рук простой stringпараметр URL-адреса и его самостоятельное преобразование в массив:

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

Если при этом вы получаете ошибку синтаксического анализа (из-за того, что кто-то передал вам искаженный массив), вы можете заставить обработчик исключений возвращать 400 Bad Requestошибку вместо стандартной, более недружественной 404 Not Foundошибки, которую MVC возвращает, когда конечная точка не найдена.

Гансинатор
источник
9

Вы также можете использовать этот формат URL, и ASP.NET MVC сделает все за вас. Но не забудьте применить кодировку URL.

?param1[0]=3344&param1[1]=2222
Cioxideru
источник
6

Я не знаю, откуда взялась строка URL-адреса Groky, но у меня была такая же проблема с некоторым javascript, вызывающим мой контроллер / действие. Он будет создавать URL из null1 или многих «идентификаторов» из списка с множественным выбором (который уникален для решения, которым я собираюсь поделиться).

Я скопировал / вставил привязку пользовательской модели Дарина и украсил свое действие / параметр, но это не сработало. Меня все еще nullценили int[] ids. Даже в «безопасном» случае, когда у меня действительно было много идентификаторов.

Я закончил тем, что изменил javascript, чтобы создать дружественный массив параметров ASP.NET MVC, например

?ids=1&ids=2

Я должен был сделать несколько глупостей, хотя

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

Итак, полный блок был

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

Это грязно, но мне впервые пришлось так взламывать javascript.

Энтони Мастрян
источник
2
Просто любопытно, нет ли в последнем предложении перед словом «первый раз» пропущенное «не». В противном случае вам повезет!
DCShannon
1
@DCShannon: ха-ха, я понимаю твою точку зрения! Но это был мой первый раз. Я не уверен, нормально ли это или я переборщил.
Энтони Мастрян,
1

.Net Core Ответ

Для тех, кто приезжает сюда в последнее время, вы можете сделать это в .Net Core с помощью:

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

и:

public ActionResult Multiple([FromQuery] int[] ids)
{
    ...
}
Красный
источник