Como aceito uma matriz como um parâmetro de ação do controlador ASP.NET MVC?


86

Eu tenho um controlador ASP.net MVC chamado Designsque tem uma ação com a seguinte assinatura:

public ActionResult Multiple(int[] ids)

No entanto, quando tento navegar para esta ação usando o url:

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

O idsparâmetro é sempre nulo. Existe alguma maneira de fazer o MVC converter o ?ids=parâmetro de consulta de URL em um array para a ação? Já vi falar sobre o uso de um filtro de ação, mas pelo que posso dizer, isso só funcionará para POSTs em que o array é passado nos dados da solicitação, e não na própria URL.

Respostas:


153

O fichário de modelo padrão espera este url:

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

para se ligar com sucesso a:

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

E se você quiser que isso funcione com valores separados por vírgulas, você pode escrever um fichário de modelo personalizado:

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

e então você pode aplicar este fichário de modelo a um argumento de ação específico:

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

ou aplique-o globalmente a todos os parâmetros de matriz de inteiros Application_Startem Global.asax:

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

e agora sua ação de controle pode ser parecida com esta:

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

2
Eu estava faltando [FromUri]. public ActionResult Multiple([FromUri]int[] ids) {} (GET)
C0d1ngJammer

1
@Darin existe uma maneira de aplicar a vinculação personalizada globalmente, mas apenas ignorar para uma ação específica? Não consegui encontrar uma maneira de fazer isso: stackoverflow.com/questions/45379040/…
akd

15

Para estender a resposta de Darin Dimitrov , algo que você pode fazer é aceitar um stringparâmetro simples no seu URL e convertê-lo em um array você mesmo:

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

Se você obtiver um erro de análise ao fazer isso (porque alguém lhe passou um array malformado), pode fazer com que seu manipulador de exceção retorne um 400 Bad Requesterro em vez do erro padrão, mais hostil 404 Not Foundque o MVC retorna quando um ponto de extremidade não é encontrado.


9

Você também pode usar este formato de URL, e a ASP.NET MVC fará tudo por você. Mas lembre-se de aplicar a codificação de URL.

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

6

Não sei de onde vinha a string de URL do Groky, mas tive o mesmo problema com algum javascript chamando meu controlador / ação. Ele criaria um URL de null1 ou muitos "IDs" de uma lista de seleção múltipla (que é exclusiva para a solução que irei compartilhar).

Copiei / colei o fichário de modelo personalizado de Darin e decorei minha ação / parâmetro, mas não funcionou. Eu ainda fui nullvalorizado int[] ids. Mesmo no caso "seguro" em que eu realmente tinha muitos IDs.

Acabei mudando o javascript para produzir um array de parâmetros amigável para ASP.NET MVC como

?ids=1&ids=2

Eu tive que fazer algumas coisas bobas, embora

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

Então, todo o bloco foi

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

É confuso, mas é a primeira vez que tenho que hackear assim em javascript.


2
Apenas curiosidade se há um 'não' ausente antes da "primeira vez" na última frase. Caso contrário, sorte sua!
DCShannon

1
@DCShannon: haha, entendo seu ponto! Mas, esta foi minha primeira vez. Não tenho certeza se esse tipo de coisa é normal ou se estou exagerando.
Anthony Mastrean,

1

.Net Core Answer

Para aqueles que vêm aqui recentemente, você pode fazer isso no .Net Core com:

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

e:

public ActionResult Multiple([FromQuery] int[] ids)
{
    ...
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.