Opcjonalne parametry w routingu atrybutów Web Api

90

Chcę obsłużyć POST następującego wywołania API:

/v1/location/deviceid/appid

Dodatkowe parametry pochodzą z post-body.

To wszystko działa dobrze dla mnie. Teraz chcę rozszerzyć mój kod, zezwalając „deviceid” i / lub „appid” i / lub BodyData na null:

/v1/location/deviceid
/v1/location/appid
/v1/location/

Te 3 adresy URL powinny odpowiadać tą samą drogą.

Moje pierwsze podejście (wymagane BodyData):

[Route("v1/location/{deviceid}/{appid}", Name = "AddNewLocation")]
public location_fromuser Post(string deviceid = null, string appid = null, [FromBody] location_fromuser BodyData)
{
    return repository.AddNewLocation(deviceid, appid, BodyData);
}

To nie działa i zwraca błąd kompilacji:

„Opcjonalne parametry muszą znajdować się na końcu”

Następna próba:

[Route("v1/location/{deviceid}/{appid}", Name = "AddNewLocation")]
public location_fromuser Post([FromBody] location_fromuser BodyData, string deviceid = null, string appid = null)

Teraz moja funkcja AddNewLocation () otrzymuje zawsze BodyData=null- nawet jeśli wywołanie wysyła Body.

Na koniec ustawiłem wszystkie 3 parametry jako opcjonalne:

[Route("v1/location/{deviceid}/{appid}", Name = "AddNewLocation")]
public location_fromuser Post(string deviceid = null, string appid = null, [FromBody location_fromuser BodyData = null)

Nie działa:

Opcjonalny parametr BodyDatanie jest obsługiwany przez FormatterParameterBinding.

Dlaczego chcę rozwiązania z opcjonalnymi parametrami? Mój kontroler obsługuje tylko „dodawanie nowej lokalizacji” przez POST.

Chcę wysyłać własne wyjątki lub komunikaty o błędach w przypadku złych danych. Nawet jeśli w wywołaniu brakuje wartości. W takim przypadku chcę móc zdecydować o rzuceniu wyjątku lub ustawieniu wartości domyślnych przez mój kod.

Oliver Apel
źródło

Odpowiedzi:

177

W przypadku przychodzących żądań, takich jak /v1/location/1234, jak można sobie wyobrazić, interfejsowi API sieci Web trudno byłoby automatycznie ustalić, czy wartość segmentu odpowiadającego wartości „1234” jest powiązana, appidczy nie deviceid.

Myślę, że powinieneś zmienić swój szablon trasy, aby był podobny, [Route("v1/location/{deviceOrAppid?}", Name = "AddNewLocation")]a następnie przeanalizować, deiveOrAppidaby znaleźć typ identyfikatora.

Należy również ustawić segmenty w samym szablonie trasy jako opcjonalne, w przeciwnym razie segmenty będą uznawane za wymagane. Zwróć uwagę na ?postać w tym przypadku. Na przykład: [Route("v1/location/{deviceOrAppid?}", Name = "AddNewLocation")]

Kiran Challa
źródło
56
?wewnątrz szablonu trasy jest to, czego szukałem. +1
Kal_Torak
4
Nie powiedziałbym, że „deviceOrAppId” jest najlepszym wyborem projektowym. Myślę, że API powinno zawsze wiedzieć z definicji, co otrzyma, jeśli to w ogóle możliwe.
Niels Brinch,
14
Tylko dla informacji - Gdy oznaczymy parametr jako opcjonalny w uri akcji za pomocą ?znaku, to musimy podać domyślne wartości parametrów w sygnaturze metody, np. MyMethod (string name = "someDefaultValue", int? Id = null).
RBT
@RBT you da real MVP, utknąłem tam na minutę. Dziękuję Ci!
sm
1
Fajne. Cieszę się, że pomogło Ci @sm Przekształciłem mój komentarz w odpowiedź, aby uzyskać lepszą widoczność, ponieważ wydaje się pomocna. Będzie to dodatek do posta Kirana.
RBT
45

Inna informacja: jeśli chcesz użyć ograniczenia trasy , wyobraź sobie, że chcesz, aby parametr miał typ danych int , a następnie użyj tej składni:

[Route("v1/location/**{deviceOrAppid:int?}**", Name = "AddNewLocation")]

The ? znak jest umieszczany zawsze przed ostatnim } znakiem

Aby uzyskać więcej informacji, zobacz: Opcjonalne parametry URI i wartości domyślne

Tiago Ferreira
źródło
18

Przekształcenie mojego komentarza w odpowiedź uzupełniającą odpowiedź @Kiran Chala, która wydaje się pomocna dla publiczności:

Kiedy oznaczymy parametr jako opcjonalny w uri akcji za pomocą ?znaku, musimy podać domyślne wartości parametrów w sygnaturze metody, jak pokazano poniżej:

MyMethod(string name = "someDefaultValue", int? Id = null)

RBT
źródło
Już miałem to skomentować.
Jnr