Comment accepter un tableau comme un ASP.NET paramètre D'action du contrôleur MVC?

j'ai un ASP.net contrôleur MVC appelé Designs qui a une action avec la signature suivante:

public ActionResult Multiple(int[] ids)

cependant, quand j'essaie de naviguer vers cette action en utilisant l'url:

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

le paramètre ids est toujours nul. Y a-t-il un moyen d'amener MVC à convertir le paramètre de requête de L'URL ?ids= en un tableau pour l'action? J'ai vu parler de l'utilisation d'un filtre d'actions, mais aussi loin que je peux dire que ne travaillez pour les messages où le tableau est passé dans les données de la requête plutôt que dans L'URL elle-même.

65
demandé sur Michael 2012-03-01 03:22:28

4 réponses

le modèle par défaut binder attend cette url:

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

afin de lier avec succès à:

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

et si vous voulez que cela fonctionne avec des valeurs séparées par des virgules, Vous pouvez écrire un classeur de modèle personnalisé:

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

et alors vous pourriez appliquer ce modèle de reliure à un argument d'action particulier:

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

ou l'appliquer globalement à tous les paramètres du tableau entier dans votre Application_Start dans Global.asax :

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

et maintenant votre action de contrôleur pourrait ressembler à ceci:

public ActionResult Multiple(int[] ids)
{
    ...
}
121
répondu Darin Dimitrov 2013-09-19 13:54:26

vous pouvez également utiliser ce format D'URL, et ASP.NET MVC fera tout pour vous. Mais, n'oubliez pas d'appliquer l'encodage D'URL.

?param1[0]=3344&param1[1]=2222
9
répondu Cioxideru 2013-09-19 13:55:17

pour étendre sur réponse de Darin Dimitrov , quelque chose que vous pouvez obtenir avec est d'accepter un simple string dans votre paramètre URL et de le convertir en un tableau vous-même:

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

si vous obtenez une erreur d'analyse pendant que vous faites ceci (parce que quelqu'un vous a passé un tableau malformé), vous pouvez faire retourner une erreur 400 Bad Request à la place de l'erreur par défaut, plus inamovible 404 Not Found que MVC retourne quand un point de terminaison n'est pas trouvé.

8
répondu TheHansinator 2017-05-23 12:09:57

Je ne sais pas d'où venait la chaîne D'URL de Groky, mais j'ai eu le même problème avec du javascript appelant mon controller/action. Il construirait une URL de null , 1, ou de nombreux" IDs " à partir d'une liste de sélection multiple (ce qui est unique à la solution que je vais partager).

j'ai copié/collé le classeur modèle personnalisé de Darin et décoré mon action/paramètre, mais cela n'a pas fonctionné. J'ai toujours null valorisé int[] ids . Même dans le cas" sûr " où je en fait n'ont de nombreuses Id.

j'ai fini par changer le javascript pour produire un ASP.NET MVC friendly parameter array like

?ids=1&ids=2

j'ai dû faire quelques silly stuff, si la

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

donc, le bloc complet était

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

c'est bordélique, mais c'est la première fois que je dois pirater comme ça en javascript.

5
répondu Anthony Mastrean 2013-09-19 15:52:17