¿Cómo acepto una matriz como parámetro de acción del controlador ASP.NET MVC?

85

Tengo un controlador ASP.net MVC llamado Designsque tiene una acción con la siguiente firma:

public ActionResult Multiple(int[] ids)

Sin embargo, cuando intento navegar a esta acción usando la URL:

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

El idsparámetro siempre es nulo. ¿Hay alguna forma de hacer que MVC convierta el ?ids=parámetro de consulta de URL en una matriz para la acción? He visto hablar de usar un filtro de acción, pero por lo que puedo decir, eso solo funcionará para POST donde la matriz se pasa en los datos de solicitud en lugar de en la URL en sí.

Grokys
fuente

Respuestas:

150

El enlazador de modelos predeterminado espera esta URL:

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

para enlazar con éxito a:

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

Y si desea que esto funcione con valores separados por comas, puede escribir una carpeta de modelos personalizada:

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

y luego podría aplicar este enlazador de modelos a un argumento de acción en particular:

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

o aplíquelo globalmente a todos los parámetros de matriz de enteros en su Application_Starten Global.asax:

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

y ahora la acción de su controlador podría verse así:

public ActionResult Multiple(int[] ids)
{
    ...
}
Darin Dimitrov
fuente
2
Me estaba perdiendo [FromUri]. public ActionResult Multiple([FromUri]int[] ids) {} (GET)
C0d1ngJammer
1
@Darin, ¿hay alguna manera de aplicar el enlace personalizado globalmente pero simplemente ignorarlo para una acción específica? No pude encontrar una manera de hacerlo: stackoverflow.com/questions/45379040/…
akd
15

Para ampliar la respuesta de Darin Dimitrov , algo con lo que puede salirse con la suya es aceptar un stringparámetro simple en su URL y convertirlo en una matriz usted mismo:

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

Si obtiene un error de análisis al hacer esto (porque alguien le pasó una matriz mal formada), puede hacer que su controlador de excepciones devuelva un 400 Bad Requesterror en lugar del error predeterminado, más hostil 404 Not Foundque MVC devuelve cuando no se encuentra un punto final.

TheHansinator
fuente
9

También puede usar este formato de URL y ASP.NET MVC hará todo por usted. Pero recuerde aplicar la codificación de URL.

?param1[0]=3344&param1[1]=2222
Cioxideru
fuente
6

No sé de dónde venía la cadena de URL de Groky, pero tuve el mismo problema con algunos javascript llamando a mi controlador / acción. Construiría una URL denull , 1 o muchas "ID" de una lista de selección múltiple (que es única para la solución que voy a compartir).

Copié / pegué la carpeta de modelos personalizados de Darin y decoré mi acción / parámetro, pero no funcionó. Todavía me nullvaloran int[] ids. Incluso en el caso "seguro" en el que en realidad tenía muchas identificaciones.

Terminé cambiando el javascript para producir una matriz de parámetros amigable ASP.NET MVC como

?ids=1&ids=2

Aunque tuve que hacer algunas tonterías

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

Entonces, el bloque completo fue

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

Es complicado, pero es la primera vez que tengo que piratear así en javascript.

Anthony Mastrean
fuente
1
Solo tengo curiosidad por saber si falta un "no" antes de "primera vez" en la última oración. De lo contrario, ¡suerte!
DCShannon
1
@DCShannon: jaja, ¡veo tu punto! Pero esta fue mi primera vez. No estoy seguro de si este tipo de cosas es normal o si me estoy excediendo.
Anthony Mastrean
0

Respuesta de .Net Core

Para aquellos que vienen aquí en los últimos tiempos, pueden hacer esto en .Net Core con:

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

y:

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