Wie akzeptiere ich ein Array als Aktionsparameter für den ASP.NET MVC-Controller?

83

Ich habe einen ASP.net MVC-Controller namens Designs, der eine Aktion mit der folgenden Signatur hat:

public ActionResult Multiple(int[] ids)

Wenn ich jedoch versuche, mithilfe der URL zu dieser Aktion zu navigieren:

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

Der idsParameter ist immer null. Gibt es eine Möglichkeit, MVC dazu zu bringen, den ?ids=URL-Abfrageparameter in ein Array für die Aktion zu konvertieren ? Ich habe die Rede von der Verwendung eines Aktionsfilters gesehen, aber soweit ich das beurteilen kann, funktioniert dies nur für POSTs, bei denen das Array in den Anforderungsdaten und nicht in der URL selbst übergeben wird.

Grokys
quelle

Antworten:

150

Der Standardmodellordner erwartet diese URL:

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

um erfolgreich zu binden an:

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

Wenn dies mit durch Kommas getrennten Werten funktionieren soll, können Sie einen benutzerdefinierten Modellordner schreiben:

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

und dann können Sie diesen Modellordner auf ein bestimmtes Aktionsargument anwenden:

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

oder wenden Sie es global auf alle ganzzahligen Array-Parameter in Ihrem Application_StartIn an Global.asax:

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

und jetzt könnte Ihre Controller-Aktion folgendermaßen aussehen:

public ActionResult Multiple(int[] ids)
{
    ...
}
Darin Dimitrov
quelle
2
Ich fehlte [FromUri]. public ActionResult Multiple([FromUri]int[] ids) {} (GET)
C0d1ngJammer
1
@Darin gibt es eine Möglichkeit, die benutzerdefinierte Bindung global anzuwenden, aber für bestimmte Aktionen einfach zu ignorieren? Ich konnte nicht einen Weg finden , das zu tun: stackoverflow.com/questions/45379040/...
akd
15

Um die Antwort von Darin Dimitrov zu erweitern , können Sie einen einfachen stringParameter in Ihrer URL akzeptieren und selbst in ein Array konvertieren:

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

Wenn Sie dabei einen Analysefehler erhalten (weil Ihnen jemand ein fehlerhaftes Array übergeben hat), kann Ihr Ausnahmebehandler einen 400 Bad RequestFehler anstelle des standardmäßigen, unfreundlicheren 404 Not FoundFehlers zurückgeben, den MVC zurückgibt, wenn kein Endpunkt gefunden wird.

Der Hansinator
quelle
9

Sie können auch dieses URL-Format verwenden, und ASP.NET MVC erledigt alles für Sie. Denken Sie jedoch daran, die URL-Codierung anzuwenden.

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

Ich weiß nicht, woher Grokys URL-Zeichenfolge stammt, aber ich hatte das gleiche Problem mit einem Javascript, das meinen Controller / meine Aktion aufruft. Es würde eine URL von null, 1 oder vielen "IDs" aus einer Mehrfachauswahlliste erstellen (was nur für die Lösung gilt, die ich freigeben werde).

Ich habe Darins benutzerdefinierten Modellordner kopiert / eingefügt und meine Aktion / meinen Parameter dekoriert, aber es hat nicht funktioniert. Ich wurde immer noch nullgeschätzt int[] ids. Selbst in dem "sicheren" Fall, in dem ich tatsächlich viele Ausweise hatte.

Am Ende habe ich das Javascript geändert, um ein ASP.NET MVC-freundliches Parameterarray wie zu erzeugen

?ids=1&ids=2

Ich musste allerdings ein paar dumme Sachen machen

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

Also war der ganze Block

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

Es ist chaotisch, aber es ist das erste Mal, dass ich so in Javascript hacken musste.

Anthony Mastrean
quelle
1
Nur neugierig, ob in diesem letzten Satz vor dem "ersten Mal" ein "Nicht" fehlt. Ansonsten viel Glück!
DCShannon
1
@DCShannon: haha, ich verstehe deinen Standpunkt! Aber das war mein erstes Mal. Ich bin mir nicht sicher, ob so etwas normal ist oder ob ich über Bord gehe.
Anthony Mastrean