Come si accetta un array come parametro di azione del controller ASP.NET MVC?


86

Ho un controller ASP.net MVC chiamato Designsche ha un'azione con la seguente firma:

public ActionResult Multiple(int[] ids)

Tuttavia, quando provo a passare a questa azione utilizzando l'URL:

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

Il idsparametro è sempre nullo. Esiste un modo per ottenere MVC per convertire il ?ids=parametro di query dell'URL in un array per l'azione? Ho visto parlare dell'utilizzo di un filtro di azione ma per quanto ne so funzionerà solo per i POST in cui l'array viene passato nei dati della richiesta piuttosto che nell'URL stesso.

Risposte:


153

Il raccoglitore di modelli predefinito prevede questo URL:

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

per legarsi con successo a:

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

E se vuoi che funzioni con valori separati da virgole, puoi scrivere un raccoglitore di modelli personalizzato:

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 quindi potresti applicare questo legante di modelli a un particolare argomento di azione:

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

o applicalo globalmente a tutti i parametri dell'array intero nel tuo Application_Startin Global.asax:

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

e ora l'azione del controller potrebbe essere simile a questa:

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

2
Mi mancava [FromUri]. public ActionResult Multiple([FromUri]int[] ids) {} (GET)
C0d1ngJammer

1
@Darin c'è un modo per applicare l'associazione personalizzata a livello globale ma ignorarlo per un'azione specifica? Non sono riuscito a trovare un modo per farlo: stackoverflow.com/questions/45379040/…
akd

15

Per estendere la risposta di Darin Dimitrov , qualcosa con cui puoi farla franca è accettare un semplice stringnel tuo parametro URL e convertirlo tu stesso in un array:

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

Se si verifica un errore di analisi durante questa operazione (perché qualcuno ti ha passato un array non valido), puoi fare in modo che il gestore delle eccezioni restituisca un 400 Bad Requesterrore invece dell'errore predefinito, più ostile 404 Not Foundche MVC restituisce quando non viene trovato un endpoint.


9

Puoi anche utilizzare questo formato URL e ASP.NET MVC farà tutto per te. Ricorda però di applicare la codifica URL.

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

6

Non so da dove provenisse la stringa URL di Groky, ma ho avuto lo stesso problema con alcuni javascript che chiamavano il mio controller / azione. Creerebbe un URL di null, 1 o molti "ID" da un elenco a selezione multipla (che è unico per la soluzione che condividerò).

Ho copiato / incollato il raccoglitore di modelli personalizzato di Darin e decorato la mia azione / parametro, ma non ha funzionato. Sono ancora stato nullvalutato int[] ids. Anche nel caso "sicuro" in cui effettivamente avevo molti ID.

Ho finito per cambiare il javascript per produrre un array di parametri amichevole ASP.NET MVC come

?ids=1&ids=2

Però ho dovuto fare delle cose stupide

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

Quindi, l'intero blocco era

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

È disordinato, ma è la prima volta che devo hackerare in questo modo in javascript.


2
Sono solo curioso di sapere se manca un "non" prima della "prima volta" nell'ultima frase. Altrimenti, sei fortunato!
DCShannon

1
@DCShannon: haha, capisco il tuo punto! Ma questa è stata la mia prima volta. Non sono sicuro se questo genere di cose sia normale o se sto esagerando.
Anthony Mastrean

1

.Net Core Risposta

Per coloro che sono venuti qui negli ultimi tempi, puoi farlo in .Net Core con:

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

e:

public ActionResult Multiple([FromQuery] int[] ids)
{
    ...
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.