Jak zaakceptować tablicę jako parametr akcji kontrolera ASP.NET MVC?

85

Mam kontroler ASP.net MVC o nazwie, Designsktóry ma akcję z następującym podpisem:

public ActionResult Multiple(int[] ids)

Jednak gdy próbuję przejść do tej akcji za pomocą adresu URL:

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

idsParametr jest zawsze zerowa. Czy istnieje sposób, aby MVC przekonwertował ?ids=parametr zapytania adresu URL na tablicę dla akcji? Widziałem rozmowy o używaniu filtru akcji, ale o ile wiem, będzie to działać tylko w przypadku POST, w których tablica jest przekazywana w danych żądania, a nie w samym adresie URL.

Grokys
źródło

Odpowiedzi:

150

Domyślny spinacz modelu oczekuje tego adresu URL:

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

aby pomyślnie połączyć się z:

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

A jeśli chcesz, aby to działało z wartościami oddzielonymi przecinkami, możesz napisać niestandardowy spinacz modelu:

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

a następnie możesz zastosować ten spinacz modelu do określonego argumentu akcji:

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

lub zastosuj go globalnie do wszystkich parametrów tablic całkowitych w swoim Application_Startin Global.asax:

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

a teraz twoja akcja kontrolera może wyglądać tak:

public ActionResult Multiple(int[] ids)
{
    ...
}
Darin Dimitrov
źródło
2
Brakowało mi [FromUri]. public ActionResult Multiple([FromUri]int[] ids) {} (GET)
C0d1ngJammer
1
@Darin Czy istnieje sposób na globalne zastosowanie niestandardowego powiązania, ale po prostu zignoruj ​​go dla określonej akcji? Nie mogłem znaleźć sposobu, aby to zrobić: stackoverflow.com/questions/45379040/…
akd
15

Aby rozszerzyć odpowiedź Darina Dimitrova , coś, co może ujść na sucho, to zaakceptowanie prostego stringparametru w adresie URL i samodzielne przekonwertowanie go na tablicę:

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

Jeśli podczas wykonywania tej czynności wystąpi błąd analizy (ponieważ ktoś przekazał Ci zniekształconą tablicę), możesz spowodować, że program obsługi wyjątków zwróci 400 Bad Requestbłąd zamiast domyślnego, bardziej nieprzyjaznego 404 Not Foundbłędu, który MVC zwraca, gdy punkt końcowy nie zostanie znaleziony.

TheHansinator
źródło
9

Możesz również użyć tego formatu adresu URL, a ASP.NET MVC zrobi wszystko za Ciebie. Pamiętaj jednak, aby zastosować kodowanie adresów URL.

?param1[0]=3344&param1[1]=2222
Cioxideru
źródło
6

Nie wiem, skąd pochodzi ciąg adresu URL Groky, ale miałem ten sam problem z jakimś javascriptem wywołującym mój kontroler / akcję. Utworzyłoby to adres URLnull , 1 lub wiele „identyfikatorów” z listy wielokrotnego wyboru (która jest unikalna dla rozwiązania, które zamierzam udostępnić).

Skopiowałem / wkleiłem spoiwo modelu Darina i ozdobiłem moje działanie / parametr, ale to nie zadziałało. Nadal jestem nullcenionyint[] ids . Nawet w „bezpiecznym” przypadku, w którym faktycznie miałem wiele dokumentów.

Skończyło się na zmianie javascript, aby utworzyć przyjazną tablicę parametrów ASP.NET MVC, taką jak

?ids=1&ids=2

Musiałem jednak zrobić trochę głupich rzeczy

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

A więc cały blok był

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

To niechlujne, ale po raz pierwszy musiałem tak hakować w javascript.

Anthony Mastrean
źródło
1
Ciekawe, czy w ostatnim zdaniu brakuje „nie” przed „pierwszym razem”. W przeciwnym razie masz szczęście!
DCShannon,
1
@DCShannon: haha, rozumiem twój punkt widzenia! Ale to był mój pierwszy raz. Nie jestem pewien, czy takie rzeczy są normalne, czy też przesadzę.
Anthony Mastrean
0

Odpowiedź .Net Core

Dla tych, którzy przyjeżdżają tu ostatnio, możesz to zrobić w .Net Core za pomocą:

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

i:

public ActionResult Multiple([FromQuery] int[] ids)
{
    ...
}
Czerwony
źródło