REST API status w przypadku błędu

1

Witam,

Jaki kod odpowiedzi HTTP powinien otrzymać request, który został obsłużony po stronie aplikacji ale wystąpiły pewne błędy uniemożliwiające jego prawidłowe przeprocesowanie.

Przykład:

Request, który aktualizuje ceny produktów w sklepie.
Jeśli:

  • produkt istnieje, to zwracam kod odpowiedzi 200
  • produkt nie istnieje, to powinienem zwrócić 200 + w response wstawić np. błąd jaki wywaliła aplikacja, czy może bawić się ze statusami z grupy 4XX?

Jakie macie dobre praktyki w tym temacie?

4
  1. W drugim zdecydowanie 404. Inaczej to nie jest REST, ale inna technolgia (np GraphQL )

  2. Nie wyważasz otwartych drzwi? Kody dla REST są powszechnie udokumentowane (3xx, 4xx, 5xx) - o ile to nadal jest (moje słowo) nie zgwałcony REST (bo wg mojego prywatnego rankingu wiele jest "zgwałconego")

2

Skoro GET /api/product/<id> zwróci 404 (bo nie ma produktu) to PUTPATCH /api/product/<id> tym bardziej powinien.

EDIT: uwagi niżej

3

Poczytaj o statusach HTTP, na Wiki masz pełną listę

0

Dziękuję,

Jest to jak najbardziej zbieżne z informacjami z innego źródła. :)

2

Z tym że warto zwrócić uwagę (na przyszłość) że nie zawsze "nie znaleziono" = 404. Przykład: jeśli implementujesz szukajkę, to w przypadku nie znalezienia rezultatu wg danych kryteriów 404 nie ma sensu, bo zapytanie się powiodło i jego rezultatem jest pusty zbiór.

3
Wiara czyni cuda napisał(a):
  • produkt istnieje, to zwracam kod odpowiedzi 200
  • produkt nie istnieje, to powinienem zwrócić 200 + w response wstawić np. błąd jaki wywaliła aplikacja, czy może bawić się ze statusami z grupy 4XX?

Jakie macie dobre praktyki w tym temacie?

Zawsze zwracam zarówno sensowny kod HTTP błędu oraz jakiś komunikat. Czasem także unikalny (w ramach API) kod błędu, aby ludzie od UI mogli to np. przetłumaczyć dla wersji językowej.

Mniej więcej idzie to tak:
201 - jeśli utworzono nowy zasób;
202 - jeśli wywołano jakąś długotrwałą operację, która nie zwraca wyniku od razu;
204 - jeśli usunięcie się powiodło;
200 - w pozostałych przypadkach powodzenia;
400 - jeśli użytkownik spieprzył;
401, 403 - wiadomo, to nawet często do aplikacji nie wchodzi; (edytowane, bo się w komentarzach czepiają)
404 - nie znaleziono;
408 - jeśli jakiś wywoływany synchronicznie zewnętrzny zasób nie odpowie w założonym czasie, i jeśli jest wymóg odróżnienia tego od 500;
409 - operacja nieprawidłowa dla danego zasobu, dla Twojego przykładu to np. zmiana ceny wycofanego produktu;
415 - nieprawidłowa wersja endpointu;
500 - każdy inny błąd, z którym użytkownik nie może niczego zrobić.

Delor napisał(a):

Skoro GET /api/product/<id> zwróci 404 (bo nie ma produktu) to PUT /api/product/<id> tym bardziej powinien.

PUT nie może zwrócić 404, co najwyżej 201.

4

A co z PUT na zasobie który nie istnieje? 404 jak dla mnie.

2

No jak nie istnieje, to PUT ten zasób tworzy i zwraca 201.

1
somekind napisał(a):

No jak nie istnieje, to PUT ten zasób tworzy i zwraca 201.

Dla PUT podajesz ID, żeby było wiadomo co ma być zmodyfikowane.
Czyli jeśli zrobię /endpoint/id/cztery_litery to ma stworzyć taką encje z takim ID? No chyba nie?
To samo jeśli jest sytuacja z inkrementacja ID i ostatnie masz 10, a tu ktoś nagle wpisze 1000.

0

Fakt. Zamiast PUT powinno być PATCH.
I o taką sytuację pytał OP.

@somekind a co zwracać gdy w PUT mam tylko część danych? Przykładowo jedynie cena produktu. Próba stworzenia takiego "wybrakowanego" zasobu jest ewidentnie błędem.
400? 409?

0

@Delor: Patch tym bardziej nie ma sensu.
No bo jak stworzysz encje z pustymi wartościami? Przykładowo wymagana jest cena i marża.
Jeśli aktualizuję cenę to wysyłam zmieniona cenę i koniec. To jak tylko po ID i cenie tworzyć encję?
Dziś mało spałem to jak bredzę śmiało kasujecie :)

2
jurek1980 napisał(a):

Czyli jeśli zrobię /endpoint/id/cztery_litery to ma stworzyć taką encje z takim ID? No chyba nie?

A gdzieś jest powiedziane, że nie może?

To samo jeśli jest sytuacja z inkrementacja ID i ostatnie masz 10, a tu ktoś nagle wpisze 1000.

ID to nie to samo, co primary key w bazie. To może być np. nazwa klienta albo jakikolwiek inny unikalny biznesowy identyfikator.

Ja generalnie jeśli wystawiam PUT to znaczy, że to użytkownik API decyduje o identyfikatorze, no ale faktycznie, dla zasobów tworzonych POSTem, czyli takich z ID nadanym przez API, to PUT może zwracać 404. Dla zasobów tworzonych PUTem, nie, bo taka sytuacja nie nastąpi.

Delor napisał(a):

@somekind a co zwracać gdy w PUT mam tylko część danych? Przykładowo jedynie cena produktu. Próba stworzenia takiego "wybrakowanego" zasobu jest ewidentnie błędem.
400? 409?

No skoro dane są niepełne, to 400 jak dla mnie.

0
somekind napisał(a):
jurek1980 napisał(a):

Czyli jeśli zrobię /endpoint/id/cztery_litery to ma stworzyć taką encje z takim ID? No chyba nie?

A gdzieś jest powiedziane, że nie może?

To samo jeśli jest sytuacja z inkrementacja ID i ostatnie masz 10, a tu ktoś nagle wpisze 1000.

ID to nie to samo, co primary key w bazie. To może być np. nazwa klienta albo jakikolwiek inny unikalny biznesowy identyfikator.

Owszem ale tracimy kontrolę nad logiką i formatem tego ID. Chodzi mi o to że jak ktoś da np. 165 literek "a" to encja się stworzy i to ID nic potem nie powie. Przyklad podany - nazwa firmy. Co taki ciąg literek aaaa powie? Nic.
Ja jednak wolę mieć nad Id jakąś kontrolę.

0
jurek1980 napisał(a):

Owszem ale tracimy kontrolę nad logiką i formatem tego ID. Chodzi mi o to że jak ktoś da np. 165 literek "a" to encja się stworzy i to ID nic potem nie powie. Przyklad podany - nazwa firmy. Co taki ciąg literek aaaa powie? Nic.

A czyj to problem? Na pewno nie mój - jeśli użytkownik sobie stworzył klienta o takiej nazwie, to go będzie miał.
No i to on dostanie opieprz za robienie rzeczy bez sensu, nie ja. :P

0

A korzystacie z błędu 422? Czy jest on zarezerwowany tylko zwracania wyniku, kiedy walidacja danych się nie powiedzie?
Np. błąd typu wprowadzenie niepoprawnych danych logowania np. hasła - co zwracacie? 400?

Spotkałem się z systemami, gdzie zawsze podanie nieprawidłowych danych zwracało 422 z customowym modelem błędów.

0

Niepoprawne logowanie to 401.
Niepoprawne dane w rodzaju brakuje jakiejś wartości, tekst zbyt długi, liczba spoza zakresu, data urodzenia w przyszłości, to 400.
422 musiałby być zwrócony po przejściu tej wstępnej walidacji żądania, gdzieś z wnętrza systemu, gdy tenże stwierdzi, że żądanie jest poprawne w sensie wymogów walidacji, ale jednak danych samych w sobie nie da się przetworzyć.
Użyłem tego może ze dwa razy.

0
lukaszek016 napisał(a):

Spotkałem się z systemami, gdzie zawsze podanie nieprawidłowych danych zwracało 422 z customowym modelem błędów.

Ten "customowy model błędów" to jest to, co ja nazywam zgwałconym REST-em
jest to owszem poprawna warstwa aplikacyjna nad HTTP/HTTPS, i nic do tego nie mam, ale już nie REST

Takie jest moje zdanie.
W pewnym dłuższym ciągu decyzyjnym powstał GraphQL, protokół mi się podoba, bo tam się nie uprawia fikcji, modele ganiają we wszystkie trzy strony (stdin, stdout, stderr) i wszystko jest jasne.

2

422 musiałby być zwrócony po przejściu tej wstępnej walidacji żądania, gdzieś z wnętrza systemu, gdy tenże stwierdzi, że żądanie jest poprawne w sensie wymogów walidacji, ale jednak danych samych w sobie nie da się przetworzyć.

@somekind RFC 4918 twierdzi, że jednak nie do końca. 400 jest wtedy kiedy wyślesz dane w niepoprawnym formacie, który nie pasuje do tego co jest zadeklarowane w Content-Type (kiedy to 406 jest używany), a 422 jak dane są poprawnie sformatowane, ale zawarte w nich dane nie są poprawne.

For example, this error condition may occur if an XML request body contains well-formed (i.e., syntactically correct), but semantically erroneous, XML instructions.

0

@hauleth: ja nie bardzo rozumiem, co tam jest "nie do końca" z tym, co ja napisałem, zwłaszcza, że specyfikacja WebDAV nie definiuje 400 w ogóle, bo to już zostało zrobione dużo wcześniej.
Ty dla błędów składni (za długi tekst, liczba spoza zakresu, data w przyszłości) stosujesz 422? To kiedy 400?

1

@somekind: 400 kiedy wysłane dane są składniowo niepoprawne. Przykładowo jeśli klient wyśle:

POST /foo HTTP/2
Content-Type: application/json

{"foo"}

To wtedy odpowiedź 400 jest odpowiednia, bo zapytanie jest składniowo niepoprawne, jeśli przejdzie przez tę walidację, to jeśli dane są niepoprawne, to wtedy 422.

0

400 jeśli klient spieprzył. :)

Pytanie zasadnicze, które powinniśmy zadać jest takie, czy wspieramy WebDAV. Bo ja raczej niekoniecznie.

Gdybym jednak chciał używać 422 w sytuacjach, o które Ci chodzi, to wtedy 400 w ogóle bym nie zwracał. Zysku zatem nie widzę.

1 użytkowników online, w tym zalogowanych: 0, gości: 1