Jaki jest prawidłowy kod odpowiedzi REST dla prawidłowego żądania, ale puste dane?

331

Na przykład uruchamiasz żądanie GET, users/9ale nie ma użytkownika o identyfikatorze nr 9. Jaki jest najlepszy kod odpowiedzi?

  • 200 OK
  • 202 Zaakceptowano
  • 204 Brak treści
  • 400 złych wniosków
  • 404 Nie Znaleziono
IMB
źródło
19
Wskazówka: znalazłeś użytkownika 9?
Chris Pfohl
42
Wskazówka 2: Więc użytkownik 9 nie został znaleziony ?
Tomasz Nurkiewicz
18
@IMB, kto mówi 204? „Brak treści” oznacza, że ​​jednostka, której szukasz, istnieje, ale nie ma reprezentacji. Na przykład, jeśli blog o identyfikatorze 15 nie ma komentarzy i nie chcesz zwrócić pustej listy dla komentarzy na blogu nr 15: „/ blog / 15 / comments” zwróci NoContent. Z drugiej strony, jeśli blog 15 istnieje, bardziej odpowiednie jest „404 Not Found”.
Chris Pfohl
12
@Crisfole nie miał na myśli „. Z drugiej strony, jeśli blog 15 nie istnieje, bardziej odpowiednie jest„ 404 Not Found ””
gdoron wspiera Monikę
15
Z pewnością zrobiłem @gdoron! :) Dzięki. Niestety spóźniłem się o około trzy lata, aby to naprawić i naprawić.
Chris Pfohl,

Odpowiedzi:

249

TL; DR: Użyj 404

Zobacz ten blog . Wyjaśnia to bardzo dobrze.

Podsumowanie komentarzy na blogu 204:

  1. 204 No Content nie jest szczególnie przydatny jako kod odpowiedzi dla przeglądarki (chociaż zgodnie ze specyfikacją HTTP przeglądarki muszą rozumieć to jako kod odpowiedzi „nie zmieniaj widoku”).
  2. 204 No Content jest jednak bardzo przydatny dla serwisów internetowych ajax, które mogą chcieć wskazać sukces bez konieczności zwracania czegoś. (Szczególnie w przypadkach takich jak DELETElub POSTs, które nie wymagają informacji zwrotnej).

Dlatego odpowiedzią na twoje pytanie jest użycie 404w twoim przypadku. 204to specjalistyczny kod odpowiedzi, którego nie powinieneś często wracać do przeglądarki w odpowiedzi naGET .

Inne kody odpowiedzi, które są jeszcze mniej odpowiednie niż 204i 404:

  1. 200należy zwrócić wraz z ciałem tego, co udało się pobrać. Nieodpowiednie, gdy pobierany element nie istnieje.
  2. 202jest używany, gdy serwer rozpoczął pracę nad obiektem, ale obiekt nie jest jeszcze w pełni gotowy. Z pewnością tak nie jest. Nie rozpocząłeś ani nie rozpoczniesz budowy użytkownika 9 w odpowiedzi na GETżądanie. To łamie wszelkie zasady.
  3. 400jest używany w odpowiedzi na źle sformatowane żądanie HTTP (na przykład zniekształcone nagłówki http, niepoprawnie uporządkowane segmenty itp.). Prawie na pewno zostanie to obsłużone przez dowolne frameworki, których używasz. Nie powinieneś sobie z tym radzić, chyba że piszesz własny serwer od zera. Edycja : nowsze wersje RFC pozwalają teraz na użycie 400 dla semantycznie niepoprawnych żądań.

Szczególnie pomocny jest opis Wikipedii kodów stanu HTTP . Możesz również zobaczyć definicje w dokumencie HTTP / 1.1 RFC2616 na www.w3.org

Chris Pfohl
źródło
8
Uwaga: kody odpowiedzi z lat 200. wskazują sukces. Kody odpowiedzi w 400s wskazują na awarię. Podsumowanie, punkt pierwszy i drugi, dotyczy 204kodu odpowiedzi (Brak treści).
Chris Pfohl,
227
-1, ponieważ zbyt mocno sprzeciwiam się 404 w odpowiedzi na udane połączenie, które nie ma rekordów do zwrócenia. Jako programista zajmujący się interfejsem API dla aplikacji innej niż internetowa zmarnowałem godziny na skontaktowanie się z programistami interfejsu API w celu ustalenia, dlaczego punkt końcowy interfejsu API, do którego dzwonię, nie istnieje, a tak naprawdę nie miał dane do zgłoszenia. Jeśli chodzi o 204, która nie jest szczególnie przydatna dla przeglądarki, to trochę przypomina ogon machający psem, ponieważ większość zastosowań punktów końcowych API w naszym wszechświecie urządzeń inteligentnych nie jest oparta na przeglądarce i te, które prawdopodobnie używają AJAX. Przepraszam, że zabrałem punkty.
Matt Cashatt,
38
@MatthewPatrickCashatt możesz głosować dowolnie. Teraz w końcu rozumiem, dlaczego ludzie mnie głoszą, ale uzasadnienie jest nadal błędne. Otrzymanie 404 nie oznacza, że ​​trasa nie ma sensu, oznacza, że ​​w tej lokalizacji nie ma zasobów. Kropka. Dzieje się tak, jeśli prosisz /badurllub /user/9gdy taki użytkownik nie istnieje. Deweloper może pomóc, dodając lepszą przyczynę niż „Nie znaleziono”, ale nie jest to wymagane.
Chris Pfohl
19
@Crisfole Jestem skłonny zgodzić (choć nie downvote, czytaj dalej), opiera się na definicji W3 z 404 , a konkretnie The server has not found anything matching the Request-URI. Serwer WWW / aplikacji w rzeczywistości znalazł AKCJĘ pasującą do identyfikatora URI żądania, chociaż serwer db nie. Jednak mówi również This status code is commonly used when [...] no other response is applicable, więc myślę, że nieco to potwierdza jego użycie (nawet jeśli się z tym nie zgadzam / lubię).
Daevin,
8
Nie sądzę, żebyś zajmował się punktem, który robię: 404 jest niebezpiecznie zagmatwany. Poleganie na dzwoniącym sprawdzającym frazę przyczyny lub treść odpowiedzi oznacza, że ​​nie ufasz kodowi stanu, w którym to przypadku nie jest to przydatne.
Adrian Baker,
364

Zdecydowanie sprzeciwiam się 404 na rzecz 204 lub 200 z pustymi danymi. Lub przynajmniej jeden powinien użyć encji odpowiedzi z 404.

Żądanie zostało odebrane i poprawnie przetworzone - wywołało kod aplikacji na serwerze, dlatego nie można tak naprawdę powiedzieć, że był to błąd klienta, a zatem cała klasa kodów błędów klienta (4xx) nie pasuje.

Co ważniejsze, 404 może się zdarzyć z wielu przyczyn technicznych. Np. Aplikacja jest tymczasowo dezaktywowana lub odinstalowywana na serwerze, problemy z połączeniem proxy i tym podobne. Dlatego klient nie może rozróżnić między 404, co oznacza „pusty zestaw wyników”, a 404, co oznacza, że ​​„nie można znaleźć usługi, spróbuj ponownie później”.

Może to być śmiertelne: wyobraź sobie usługę księgową w Twojej firmie, która zawiera listę wszystkich pracowników, którzy są objęci roczną premią. Niestety, jeden raz, kiedy się nazywa, zwraca 404. Czy to oznacza, że ​​nikt nie jest należny za premię, czy też aplikacja nie działa obecnie z powodu nowego wdrożenia?

-> W przypadku aplikacji, które dbają o jakość swoich danych, 404 bez encji odpowiedzi jest więc praktycznie niemożliwe.

Ponadto wiele platform klienckich odpowiada na błąd 404, zgłaszając wyjątek bez zadawania dalszych pytań. Zmusza to programistę klienta do wychwycenia tego wyjątku, do jego oceny, a następnie do podjęcia decyzji na podstawie tego, czy zapisać go jako błąd wykrywany np. Przez komponent monitorujący, czy też zignorować. To też nie wydaje mi się ładne.

Zaletą 404 nad 204 jest to, że może zwrócić encję odpowiedzi, która może zawierać pewne informacje o tym, dlaczego nie znaleziono żądanego zasobu. Ale jeśli to naprawdę jest istotne, można również rozważyć użycie odpowiedzi 200 OK i zaprojektować system w sposób, który pozwoli na reakcje na błędy w danych danych. Alternatywnie, można użyć ładunku odpowiedzi 404 do zwrócenia uporządkowanej informacji do dzwoniącego. Jeśli otrzyma np. Stronę HTML zamiast XML lub JSON, którą może przeanalizować, jest to dobry wskaźnik, że coś technicznego poszło nie tak zamiast odpowiedzi „brak wyniku”, która może być poprawna z punktu widzenia osoby dzwoniącej. Lub można do tego użyć nagłówka odpowiedzi HTTP.

Mimo to wolałbym 204 lub 200 z pustą odpowiedzią. W ten sposób status technicznego wykonania żądania jest oddzielony od logicznego wyniku żądania. 2xx oznacza „wykonanie techniczne ok, to jest wynik, radzimy sobie z tym”.

Myślę, że w większości przypadków klient powinien zdecydować, czy pusty wynik jest akceptowalny, czy nie. Zwracając 404 bez odpowiedzi, pomimo prawidłowego wykonania technicznego, klient może zdecydować o uznaniu przypadków za błędy, które po prostu nie są błędami.

Inna szybka analogia: Zwrócenie wartości 404 dla „nie znaleziono wyniku” jest jak zgłoszenie wyjątku DatabaseConnectionException, jeśli zapytanie SQL nie zwróci wyników. Może to zrobić zadanie, ale istnieje wiele możliwych przyczyn technicznych, które rzucają ten sam wyjątek, który następnie zostałby wzięty za prawidłowy wynik.

Jens Wurm
źródło
30
Przyczyny techniczne „nie znaleziono” są również nazywane błędami serwera. Te powinny być w 500. W szczególności: „Nie można znaleźć usługi” to 503 Service Unavailable.
Chris Pfohl
43
Pytający pytał także o konkretny zasób: pojedynczy użytkownik (nie /userstrasę, ale /users/9„Użytkownik znany jako # 9”), więc porównanie „pustego zestawu wyników” nie ma sensu. 404 oznacza, że ​​obiekt nie istnieje.
Chris Pfohl
29
404 po prostu wskazuje, że żądany zasób (w tym przypadku użytkownik o numerze 9) nie został znaleziony. Nie ma to nic wspólnego z uruchomieniem kodu aplikacji, nie ma związku z odpowiedzią aplikacji zaplecza. Pytanie dotyczyło serwera WWW, w pytaniu nie było mowy o odwrotnym proxy.
Chris Pfohl,
29
Rozumowanie w tej odpowiedzi jest przerażająco błędne.
Kurt Spindler
20
404: klient był w stanie komunikować się z danym serwerem, ale serwer nie mógł znaleźć żądanego. Dosłownie: żądanie otrzymane, było w porządku i poprawnie sformatowane (złe żądanie 400), zostało uwierzytelnione lub publiczne (nieautoryzowane 401 / zabronione 403), nie jest wymagana płatność (wymagana płatność 402), metoda żądania była w porządku (metoda niedozwolona 405 ), żądanie zaakceptowania może być spełnione (niedopuszczalne 406). 200 Ok powinno być użyte, gdy użytkownik otrzyma zasób. Pusty nie jest zasobem. 400 zakres jest dla błędów klienta 500 zakres jest dla błędów serwera Krótko mówiąc: Twoje rozumowanie jest wyłączone.
Derk-Jan
62

Na początku myślałem, że 204 ma sens, ale po dyskusjach uważam, że 404 to jedyna prawdziwa poprawna odpowiedź. Rozważ następujące dane:

Użytkownicy: John, Peter

METHOD  URL                      STATUS  RESPONSE
GET     /users                   200     [John, Peter]
GET     /users/john              200     John
GET     /users/kyle              404     Not found
GET     /users?name=kyle`        200     []
DELETE  /users/john              204     No Content

Niektóre tło:

  1. wyszukiwanie zwraca tablicę, po prostu nie miała żadnych dopasowań, ale ma treść: pusta tablica .

  2. 404 jest oczywiście najlepiej znany z adresów URL, które nie są obsługiwane przez żądany serwer, ale brakujący zasób jest w rzeczywistości taki sam.
    Mimo że /users/:namejest dopasowany users/kyle, użytkownik Kyle nie jest zasobem dostępnym, więc 404 nadal obowiązuje. To nie jest wyszukiwane hasło, to bezpośrednie odwołanie do dynamicznego adresu URL , więc jest 404.

W każdym razie moje dwa centy :)

Justus Romijn
źródło
9
Rzucam ciężar za tym stylem dla interfejsu API REST. No klient powinien poprosić o / Użytkownicy / Kyle chyba, że powiedziano mi, że zasób istniałby poprzez wywołanie albo / użytkowników lub / użytkowników name = Kyle?
Gary Barker
@GaryBarker Ale ponieważ jest to „wejście użytkownika” w pewnym sensie, API wciąż musi sobie z tym poradzić. Chociaż powinieneś zapobiegać 404 w SWOIM kliencie, nie możesz być pewien, że tak naprawdę został sprawdzony w pierwszej kolejności. Projektowanie interfejsu API powinno odbywać się bez uwzględnienia dokładnej implementacji klienta, szczególnie pod względem sprawdzonego wkładu użytkownika.
RicoBrassers,
Z tą odpowiedzią najbardziej się zgadzam. Doskonały przykład tego, jak chcę, aby interfejsy API działały. Komentarz GaryBarkera jest również idealny. Jest to błąd, gdy wyszukujesz coś według ID i nie istnieje. Przed nawiązaniem połączenia powinieneś wiedzieć, że istnieje identyfikator. Głównie dlatego, że uzyskanie „udanej” pustej odpowiedzi po prostu wykopuje błąd w dalszej części drogi, prawdopodobnie niektórzy „nie mogą odczytać nazwy właściwości niezdefiniowanej” podczas wykonywania user.name lub czegoś takiego.
Jamie Twells
Co z kombinacją tych dwóch. Zakładając, że użytkownicy mają znajomych. / users / kyle / friends? name = fred. Kyle nie istnieje, więc czy odpowiedź wynosiłaby 404? A może 200, bo nie obchodzi nas Kyle, zależy nam tylko na szukaniu jego przyjaciół z imieniem Fred?
JSextonn,
IMO dałoby 404, ponieważ jest to nieprawidłowe żądanie: Kyle nie istnieje, więc wyszukiwanie jego przyjaciół również nie jest możliwe.
Justus Romijn
23

Jeśli oczekuje się, że zasób istnieje, ale może być pusty, argumentowałbym, że łatwiej byłoby uzyskać 200 OK z reprezentacją wskazującą, że rzecz jest pusta.

Wolę więc, żeby / rzeczy zwróciły 200 OK z {„Items”: []} niż 204 z niczym, ponieważ w ten sposób kolekcja z 0 przedmiotami może być traktowana tak samo jak kolekcja z jednym lub więcej pozycji w nim.

Zostawiłbym 204 No Content dla PUT i DELETE, gdzie może się zdarzyć, że tak naprawdę nie ma użytecznej reprezentacji.

W przypadku, gdy / thing / 9 naprawdę nie istnieje, 404 jest odpowiednie.

j0057
źródło
1
Wygląda na to, że wolisz programować przeciwko API przy użyciu bardziej abstrakcyjnej formy zwanej RPC. Zamiast uzyskiwać dostęp do zasobów, ściśle przestrzegając czasowników i adresu URL opartego na zasobach, takiego jak customers/1/addressbookRPC, należy wywołać punkt końcowy podobny GetCustomerAddressBookdo albo odebrać dane lub zasadniczo zerowy i nie trzeba się martwić o złożoność HTTP. Oba mają swoje zalety i wady.
The Muffin Man,
2
@ Muffin Man, nie jestem pewien, jak możesz udawać, że wiesz, czy wolę REST, czy RPC, ani dlaczego jest to istotne w dyskusji na temat tego, który kod statusu powróci do żądania HTTP GET.
j0057
Miałem pewne okropne problemy z buforowaniem podczas używania 204. Chrome dziwnie potraktowałby to żądanie i nie pokazywał niczego w sieci i buforował poprzedni wynik. Nawet przy wszystkich nagłówkach bez pamięci podręcznej na świecie. Zgadzam się z tą odpowiedzią, 200 wydaje się najlepsze z pustą tablicą / obiektem przekazywanym do użytkownika.
Jack
22

W poprzednich projektach korzystałem z 404. Jeśli nie ma użytkownika 9, obiekt nie został znaleziony. Dlatego 404 nie znaleziono jest właściwe.

Ponieważ obiekt istnieje, ale nie ma danych, 204 No Content byłoby odpowiednie. Myślę, że w twoim przypadku obiekt nie istnieje.

Max
źródło
14

Zgodnie z postem w3 ,

200 OK

Żądanie się powiodło. Informacje zwrócone z odpowiedzią zależą od metody użytej w żądaniu

202 Zaakceptowano

Żądanie zostało zaakceptowane do przetworzenia, ale przetwarzanie nie zostało zakończone.

204 Brak treści

Serwer spełnił żądanie, ale nie musi zwracać treści encji i może chcieć zwrócić zaktualizowane metainformacje.

400 złych wniosków

Serwer nie mógł zrozumieć żądania z powodu nieprawidłowej składni. Klient NIE POWINIEN powtarzać żądania bez modyfikacji

401 Nieautoryzowane

Żądanie wymaga uwierzytelnienia użytkownika. Odpowiedź MUSI zawierać pole nagłówka Uwierzytelnianie WWW

404 Nie Znaleziono

Serwer nie znalazł nic pasującego do URI żądania. Nie podano żadnych wskazówek, czy stan jest tymczasowy czy trwały

JustCode
źródło
W3 post dotyczy kodów statusu HTTP. HTTP nie jest identyczny z REST. REST głównie, ale niekoniecznie, wykorzystuje HTTP jako protokół transportowy. 404 to kod błędu transportu HTTP. Jeśli REST byłby taki sam jak HTTP, czy nie powinien się nazywać HTTP?
Anne
1
@anneb Jak powiedziałeś, REST używa HTTP, więc ta odpowiedź ma sens. REST nie jest HTTP, REST i tak nie jest protokołem. REST nie musi być identyczny (lub taki sam jak HTTP), aby ta odpowiedź nie miała sensu.
Koray Tugay
@Koray Tugay: wyszukiwarka Google używa również http, więc zgodnie z tą odpowiedzią wyszukiwarka Google powinna odpowiedzieć na status 404, jeśli nic nie pasuje do wyszukiwania?
anneb
@anneb Czy wyszukiwarka Google wspomina o aplikacji RESTful? Nie sądzę, więc odpowiedź brzmi „nie”. Wracając do pierwotnego pytania i tej odpowiedzi, zamiast wpaść w króliczą dziurę. Jeśli pewnego dnia wyszukiwarka Google utworzy interfejs API RESTful (a może już go ma, nie wiem), może wrócić, 204 No Contentgdy nie znajdzie żadnych wyników. Nie 404, ma dla ciebie wynik, ale nie ma treści ..
Koray Tugay
13

Podsumowując lub upraszczając,

2xx: Dane opcjonalne: Dobrze sformatowany identyfikator URI: Kryteria nie są częścią identyfikatora URI: Jeśli kryteria są opcjonalne, można je określić w @RequestBody, a @RequestParam powinien prowadzić do 2xx. Przykład: filtruj według nazwy / statusu

4xx: Oczekiwane dane: Niepoprawny identyfikator URI: Kryteria są częścią identyfikatora URI: Jeśli kryteria są obowiązkowe, które można określić tylko w @PathVariable, powinno to prowadzić do 4xx. Przykład: wyszukiwanie według unikalnego identyfikatora.

Tak więc dla pytanej sytuacji: „users / 9” to 4xx (prawdopodobnie 404) Ale dla „users? Name = superman” powinno być 2xx (ewentualnie 204)

Ashish Singh
źródło
12

Zadawane są dwa pytania. Jeden w tytule i jeden w przykładzie. Myślę, że częściowo doprowadziło to do sporu o to, która odpowiedź jest odpowiednia.

Tytuł pytania dotyczy pustych danych. Puste dane to nadal dane, ale to nie to samo, co brak danych. Sugeruje to żądanie zestawu wyników, np. Listy, być może od/users . Jeśli lista jest pusta, nadal jest listą, dlatego najbardziej odpowiednia jest 204 (bez zawartości). Właśnie poprosiłeś o listę użytkowników i otrzymałeś jednego, akurat nie ma treści.

Podany przykład zamiast tego pyta o konkretny obiekt, użytkownika, /users/9 . Jeśli użytkownik # 9 nie zostanie znaleziony, wówczas obiekt użytkownika nie zostanie zwrócony. Poprosiłeś o określony zasób (obiekt użytkownika) i nie dostałeś go, ponieważ nie został znaleziony, więc 404 jest odpowiednie.

Myślę, że sposobem na rozwiązanie tego problemu jest użycie odpowiedzi w sposób, jakiego można się spodziewać bez dodawania instrukcji warunkowych, a następnie użycie 204, w przeciwnym razie użycie 404.

W moich przykładach mogę iterować po pustej liście bez sprawdzania, czy ma ona zawartość, ale nie mogę wyświetlić danych obiektu użytkownika na obiekcie zerowym bez zerwania czegoś lub dodania zaznaczenia, aby sprawdzić, czy jest on pusty.

Możesz oczywiście zwrócić obiekt przy użyciu wzorca zerowego, jeśli odpowiada to Twoim potrzebom, ale jest to dyskusja na inny wątek.

Niebieskie pudełko
źródło
9

Po zastanowieniu się nie powinieneś używać 404dlaczego?

W oparciu o RFC 7231 poprawny kod stanu to204

W powyższych odpowiedziach zauważyłem 1 małe nieporozumienie:

1. - zasobem jest: /users

2. /users/8- nie jest zasobem, to jest: zasób /usersz parametrem route8 , być może konsument nie może go zauważyć i nie zna różnicy, ale wydawca wie i musi to wiedzieć! ... więc musi zwrócić dokładną odpowiedź konsumentom. Kropka.

więc:

W oparciu o RFC: 404 jest niepoprawny, ponieważ zasoby /userszostały znalezione, ale logika wykonana przy użyciu parametru 8nie znalazła żadnego contentdo zwrócenia jako odpowiedzi, więc poprawna odpowiedź to:204

Główny punkt tutaj: 404nie znaleziono nawet zasobu do przetworzenia wewnętrznej logiki

204to: znalazłem zasób, logika została wykonana, ale nie znalazłem żadnych danych przy użyciu kryteriów podanych w parametrze route, więc nie mogę ci nic zwrócić. Przepraszam, sprawdź swoje kryteria i zadzwoń ponownie.

200: ok znalazłem zasób, logika została wykonana (nawet jeśli nie jestem zmuszony do niczego zwracać) weź to i użyj go do woli.

205: (najlepsza opcja odpowiedzi GET) Znalazłem zasób, logika została wykonana, mam dla ciebie trochę treści, używaj jej dobrze, o tak przy okazji, jeśli zamierzasz udostępnić to w widoku, odśwież widok, aby pokaż to.

Mam nadzieję, że to pomoże.

rodfraga
źródło
8

Istniejące odpowiedzi nie wyjaśniają, że ma to znaczenie, czy używasz parametrów ścieżki, czy parametrów zapytania.

  • W przypadku parametrów ścieżki parametr jest częścią ścieżki zasobów. W przypadku /users/9odpowiedzi należy odpowiedzieć, 404ponieważ nie znaleziono tego zasobu. /users/9jest zasobem, a wynik jest jednoznaczny lub błąd, nie istnieje. To nie jest monada.
  • W przypadku parametrów zapytania parametr nie jest częścią ścieżki zasobów. W przypadku /users?id=9odpowiedzi należy odpowiedzieć, 204ponieważ zasób /userszostał znaleziony, ale nie mógł zwrócić żadnych danych. Zasób /usersistnieje, a wynik jest n-ary, istnieje nawet, jeśli jest pusty. Jeśli idjest unikalny, jest to monada.

To, czy użyć parametrów ścieżki, czy parametrów zapytania, zależy od przypadku użycia. Preferuję parametry ścieżki dla argumentów obowiązkowych, normatywnych lub identyfikujących oraz parametry zapytań dla argumentów opcjonalnych, nienormatywnych lub przypisujących (takich jak stronicowanie, ustawienia regionalne sortowania i inne). W REST API, użyłbym /users/9nie /users?id=9przede wszystkim ze względu na możliwość zagnieżdżenia się dostać „rekordów dziecko” jak /users/9/ssh-keys/0zdobyć pierwszy klucz publiczny lub ssh /users/9/address/2, aby uzyskać trzeci adres pocztowy.

Wolę używać 404. Oto dlaczego:

  • Wezwania do stosowania metod jednoargumentowych (1 wynik) i n-ary (n wyników) nie powinny się różnić bez uzasadnionego powodu. Lubię mieć te same kody odpowiedzi, jeśli to możliwe. Liczba oczekiwanych wyników jest oczywiście różnicą, powiedzmy, że spodziewamy się, że ciało będzie obiektem (unarnym) lub tablicą obiektów (n-ary).
  • Dla n-ary zwróciłbym tablicę, a jeśli nie byłoby wyników, nie zwróciłbym żadnego zestawu (brak dokumentu), zwróciłbym pusty zestaw (pusty dokument, jak pusta tablica w JSON lub pusty element w XML ). Oznacza to, że wciąż jest 200, ale z zerowymi rekordami. Nie ma powodu, aby umieszczać te informacje na przewodzie innym niż w ciele.
  • 204jest jak voidmetoda. Nie chciałbym używać go do GETtylko dla POST, PUTi DELETE. Robię wyjątek w przypadku, GETgdy identyfikatorami są parametry zapytania, a nie parametry ścieżki.
  • Nie znajdując rekord jest jak NoSuchElementException, ArrayIndexOutOfBoundsExceptionlub coś w tym rodzaju, spowodowane przez klienta przy użyciu identyfikatora, który nie istnieje, tak, to jest to błąd klienta.
  • Z perspektywy kodu uzyskanie 204oznacza dodatkową gałąź w kodzie, której można by uniknąć. Komplikuje kod klienta, aw niektórych przypadkach również komplikuje kod serwera (w zależności od tego, czy używasz encji / modeli monad, czy zwykłych encji / modeli; zdecydowanie zalecam unikanie monod encji / modelu, może to prowadzić do nieprzyjemnych błędów, ponieważ ponieważ monady, którą uważasz, że operacja się powiodła i zwróć 200 lub 204, gdy powinieneś zwrócić coś innego).
  • Kod klienta jest łatwiejszy do napisania i zrozumienia, jeśli 2xx oznacza, że ​​serwer zrobił to, o co prosił klient, a 4xx oznacza, że ​​serwer nie zrobił tego, o co prosił klient, i to z winy klienta. Niepodanie klientowi rekordu, że klient żądany przez identyfikator jest winą klienta, ponieważ klient zażądał identyfikatora, który nie istnieje.

Last but not least: spójność

  • GET /users/9
  • PUT /users/9 i DELETE /users/9

PUT /users/9i DELETE /users/9już trzeba wrócić 204w przypadku udanej aktualizacji lub usunięcia. Co więc powinni zwrócić, gdyby użytkownik 9 nie istniał? Nie ma sensu przedstawiać tej samej sytuacji jako różnych kodów stanu w zależności od zastosowanej metody HTTP.

Poza tym nie jest to normatywny, ale kulturowy powód: jeśli 204zostanie użyte w GET /users/9następnej rzeczy, co wydarzy się w projekcie, to to, że ktoś myśli, że powrót 204jest dobry dla metod n-ary. A to komplikuje kod klienta, ponieważ zamiast po prostu sprawdzać, 2xxa następnie dekodować ciało, klient musi teraz konkretnie sprawdzić 204i w tym przypadku pominąć dekodowanie ciała. Bud, co zamiast tego robi klient? Utworzyć pustą tablicę? Dlaczego więc nie mieć tego na kablu? Jeśli klient tworzy pustą tablicę, 204 jest formą głupiej kompresji. Jeśli klient używa nullzamiast tego, otwiera się zupełnie inna puszka robaków.

Christian Hujer
źródło
3

204 jest bardziej odpowiednie. Zwłaszcza, gdy masz system alertów, który zapewnia bezpieczeństwo Twojej witryny, 404 w tym przypadku spowodowałoby zamieszanie, ponieważ nie wiesz, że niektóre alarmy 404 to błędy zaplecza lub normalne żądania, ale odpowiedź jest pusta.

刘武豪
źródło
Nie powinieneś zwracać 404, jeśli masz błąd zaplecza.
przerażające 22
3

Powiedziałbym, że żadne z nich nie jest właściwe. Jak powiedziano - np. Przez @anneb, również uważam, że część problemów wynika z używania kodu odpowiedzi HTTP do transportu statusu związanego z usługą RESTful. Wszystko, co usługa REST ma do powiedzenia na temat własnego przetwarzania, powinno być transportowane za pomocą kodów specyficznych dla REST.

1

Twierdzę, że jeśli serwer HTTP znajdzie jakąkolwiek usługę, która jest gotowa odpowiedzieć na żądanie, które zostało wysłane, nie powinien odpowiedzieć HTTP 404 - w końcu coś zostało znalezione przez serwer - chyba że zostało to wyraźnie określone przez usługa przetwarzająca żądanie.

Załóżmy na chwilę następujący adres: http://example.com/service/return/test.

  • Przypadek A polega na tym, że serwer „po prostu szuka pliku w systemie plików”. Jeśli nie jest obecny, 404jest poprawny. To samo jest prawdą, jeśli prosi jakąś usługę o dostarczenie dokładnie tego pliku, a ta usługa mówi mu, że nic o tej nazwie nie istnieje.
  • W przypadku B serwer nie działa z „prawdziwymi” plikami, ale w rzeczywistości żądanie jest przetwarzane przez inną usługę - np. Jakiś system szablonów. W tym przypadku serwer nie może wysuwać żadnych roszczeń dotyczących istnienia zasobu, ponieważ nic o nim nie wie (o ile usługa go nie poinformuje).

Bez odpowiedzi ze strony usługi wyraźnie wymagającej innego zachowania serwer HTTP może powiedzieć tylko 3 rzeczy:

  • 503 jeśli usługa, która ma obsłużyć żądanie, nie działa lub nie odpowiada;
  • 200 w przeciwnym razie serwer HTTP może faktycznie spełnić żądanie - bez względu na to, co powie później usługa;
  • 400lub 404wskazać, że nie ma takiej usługi (w przeciwieństwie do „istnieje, ale offline”) i nic innego nie znaleziono.

2)

Wracając do pytania: Myślę, że najczystszym podejściem byłoby w ogóle nie używać kodu odpowiedzi HTTP innego niż wspomniany wcześniej. Jeśli usługa jest obecna i odpowiada, kod HTTP powinien wynosić 200. Odpowiedź powinna zawierać status, który usługa zwróciła w osobnym nagłówku - tutaj usługa może powiedzieć

  • REST:EMPTYnp. jeśli został poproszony o wyszukanie czegoś i że badania wróciły puste;
  • REST:NOT FOUNDgdyby został poproszony o coś. „Podobny do identyfikatora” - może to być nazwa pliku lub zasób według identyfikatora lub pozycji nr 24 itd. - i że nie znaleziono określonego zasobu (zwykle żądano jednego zasobu i nie znaleziono go);
  • REST:INVALID jeśli jakakolwiek część żądania, które zostało wysłane, nie jest rozpoznawana przez usługę.

(zwróć uwagę, że poprzedziłem je słowem „REST:”, aby zaznaczyć fakt, że chociaż mogą mieć te same wartości lub sformułowania, co kody odpowiedzi HTTP, są zupełnie inne)

3)

Wróćmy do powyższego adresu URL i sprawdźmy przypadek B, w którym usługa wskazuje serwerowi HTTP, że sam nie obsługuje tego żądania, ale przekazuje je SERVICE. HTTP podaje tylko to, co zostało przekazane SERVICE, nie wie nic o tej return/testczęści, ponieważ jest to przedmiotem handlu SERVICE. Jeśli ta usługa jest uruchomiona, HTTP powinien powrócić, 200ponieważ rzeczywiście znalazł coś do obsługi żądania.

Status zwracany przez SERVICE(i który, jak wspomniano powyżej, chciałby zobaczyć w osobnym nagłówku), zależy od tego, czego się faktycznie oczekuje:

  • jeśli return/testpoprosi o określony zasób: jeśli istnieje, zwróć go ze statusem REST:FOUND; jeśli ten zasób nie istnieje, zwróć REST:NOT FOUND; można to rozszerzyć na zwrot, REST:GONEjeśli wiemy, że kiedyś istniał i nie wróci, a REST:MOVEDjeśli wiemy, że zniknął z Hollywood
  • jeśli return/testjest uważany za operację wyszukiwania lub filtrowania: jeśli zestaw wyników jest pusty, zwróć pusty zestaw żądanego typu i status REST:EMPTY; zestaw wyników w żądanym typie i statusREST:SUCCESS
  • jeśli return/testnie jest operacją rozpoznawaną przez SERVICE: zwróć, REST:ERRORjeśli jest całkowicie niepoprawna (np. literówka retrun/test), lub REST:NOT IMPLEMENTEDjeśli jest planowana na później.

4

To rozróżnienie jest o wiele czystsze niż mieszanie dwóch różnych rzeczy. Ułatwi to także debugowanie, a przetwarzanie będzie nieco bardziej skomplikowane, jeśli w ogóle.

  • Jeśli zostanie zwrócony HTTP 404, serwer mówi mi: „Nie mam pojęcia, o czym mówisz”. Chociaż część REST mojej prośby może być wyjątkowo dobra, szukam par'Mach we wszystkich niewłaściwych miejscach.
  • Z drugiej strony HTTP 200 i REST: ERR mówią mi, że dostałem usługę, ale zrobiłem coś złego w moim żądaniu do usługi.
  • Z HTTP 200 i REST: PUSTY, wiem, że nie zrobiłem nic złego - właściwy serwer, serwer znalazł usługę, właściwe żądanie do usługi - ale wynik wyszukiwania jest pusty.

Podsumowanie

Problem i dyskusja wynikają z faktu, że kody odpowiedzi HTTP są używane do oznaczenia stanu usługi, której wyniki są obsługiwane przez HTTP, lub do oznaczenia czegoś. nie wchodzi to w zakres samego serwera HTTP. Z powodu tej rozbieżności na pytanie nie można odpowiedzieć, a wszystkie opinie są przedmiotem wielu dyskusji.

Stan żądania przetworzonego przez usługę, a nie serwer HTTP NAPRAWDĘ NIE POWINIEN (RFC 6919) podawany przez kod odpowiedzi HTTP. Kod HTTP POWINIEN (RFC 2119) zawiera tylko informacje, które serwer HTTP może podać ze swojego zakresu: mianowicie, czy stwierdzono, że usługa przetworzyła żądanie, czy nie.

Zamiast tego NALEŻY zastosować inny sposób poinformowania konsumenta o stanie zapytania do usługi, która faktycznie przetwarza żądanie. Moja propozycja polega na tym, aby zrobić to za pomocą określonego nagłówka. Idealnie zarówno nazwa nagłówka, jak i jego treść są zgodne ze standardem, który ułatwia konsumentom pracę z tymi odpowiedziami.

dariok
źródło
2

Zgodnie z RFC7231 - strona59 ( https://tools.ietf.org/html/rfc7231#page-59 ) definicja odpowiedzi kodu stanu 404 to:

6.5.4 404 Not Found Kod stanu 404 (Not Found) wskazuje, że serwer źródłowy nie znalazł bieżącej reprezentacji zasobu docelowego lub nie chce ujawnić, że istnieje. Kod statusu 404 nie wskazuje, czy ten brak reprezentacji jest tymczasowy czy trwały; kod statusu 410 (Brak) jest preferowany w stosunku do 404, jeżeli serwer źródłowy wie, prawdopodobnie za pomocą pewnych konfigurowalnych środków, że stan prawdopodobnie będzie trwały. Odpowiedź 404 jest domyślnie buforowana; tzn. o ile nie wskazano inaczej w definicji metody lub jawnych kontrolkach pamięci podręcznej (patrz sekcja 4.2.2 [RFC7234]).

A najważniejszą rzeczą, która budzi wątpliwości, jest definicja zasobu w powyższym kontekście. Zgodnie z tą samą RFC (7231) definicja zasobu to:

Zasoby: Obiekt docelowy żądania HTTP nazywa się „zasobem”. HTTP nie ogranicza charakteru zasobu; definiuje jedynie interfejs, który można wykorzystać do interakcji z zasobami. Każdy zasób jest identyfikowany przez jednolity identyfikator zasobu (URI), jak opisano w sekcji 2.7 [RFC7230]. Gdy klient konstruuje komunikat żądania HTTP / 1.1, wysyła docelowy identyfikator URI w jednej z różnych form, jak zdefiniowano w (Sekcja 5.3 [RFC7230]). Po otrzymaniu żądania serwer rekonstruuje efektywny identyfikator URI żądania dla zasobu docelowego (sekcja 5.5 [RFC7230]). Jednym z celów projektowych HTTP jest oddzielenie identyfikacji zasobów od semantyki żądań, co jest możliwe dzięki przypisaniu semantyki żądaniom do metody żądania (sekcja 4) i kilku pól nagłówka modyfikujących żądanie (sekcja 5).

W moim rozumieniu kod stanu 404 nie powinien być używany w przypadku pomyślnego żądania GET z pustym wynikiem. (Przykład: lista bez wyniku dla konkretnego filtra)

Filipe Moisés
źródło
2

Takie rzeczy mogą być subiektywne, a po obu stronach jest kilka interesujących i różnych solidnych argumentów. Jednak [moim zdaniem] zwrot 404 za brakujące dane jest nieprawidłowy . Oto uproszczony opis, aby to wyjaśnić:

  • Żądanie: czy mogę prosić o dane?
  • Zasób (punkt końcowy interfejsu API): otrzymam to żądanie dla Ciebie tutaj [wysyła odpowiedź na potencjalne dane]

Nic się nie zepsuło, punkt końcowy został znaleziony, a tabela i kolumny zostały znalezione, więc baza danych zapytała, a dane „pomyślnie” zwróciły!

Teraz - czy ta „udana odpowiedź” zawiera dane, czy nie ma znaczenia, poprosiłeś o odpowiedź „potencjalnych” danych i ta odpowiedź z „potencjalnymi” danymi została spełniona. Puste, puste itp. Są poprawnymi danymi.

200 oznacza po prostu, że każde żądanie, które zrobiliśmy, zakończyło się powodzeniem. Żądam danych, nic nie poszło nie tak z HTTP / REST, a ponieważ dane (choć puste) zostały zwrócone, moje „żądanie danych” zakończyło się powodzeniem.

Zwróć 200 i pozwól, aby osoba żądająca obsługiwała puste dane, ponieważ uzasadnia to każdy scenariusz!

Rozważ ten przykład:

  • Żądanie: Zapytanie o tabelę „wykroczeń” o identyfikatorze użytkownika 1234
  • Zasób (punkt końcowy interfejsu API): zwraca odpowiedź, ale dane są puste

Te puste dane są całkowicie poprawne. Oznacza to, że użytkownik nie ma wykroczeń. Jest to 200, ponieważ wszystko jest ważne, ponieważ wtedy mogę zrobić:

Nie masz żadnych wykroczeń, jedz muffinkę jagodową!

Jeśli uważasz, że to 404, co mówisz? Nie można znaleźć wykroczeń użytkownika? Otóż ​​gramatycznie jest to poprawne, ale w świecie REST nie jest poprawne, jeśli sukces lub porażka dotyczy żądania. Dane „wykroczenia” dla tego użytkownika można znaleźć pomyślnie, jest zero wykroczeń - liczba rzeczywista reprezentuje prawidłowy stan.


[Bezczelna uwaga ..]

W swoim tytule podświadomie zgadzasz się, że 200 to poprawna odpowiedź:

Jaki jest prawidłowy kod odpowiedzi REST dla prawidłowego żądania, ale puste dane?


Oto kilka rzeczy, które należy wziąć pod uwagę przy wyborze kodu stanu, którego należy użyć, niezależnie od podmiotowości i trudnych wyborów:

  1. Spójność . Jeśli używasz 404 do „brak danych”, użyj go za każdym razem, gdy odpowiedź nie zwraca danych.
  2. Nie używaj tego samego statusu dla więcej niż jednego znaczenia . Jeśli zwrócisz 404, gdy nie znaleziono zasobu (np. Nie istnieje punkt końcowy interfejsu API itp.) , Nie używaj go również w przypadku braku zwracanych danych. To sprawia, że ​​radzenie sobie z odpowiedziami jest uciążliwe.
  3. Rozważ dokładnie kontekst . Co to jest „prośba”? Co chcesz osiągnąć?
James
źródło
1

Zakoduj treść odpowiedzi za pomocą wspólnego wyliczenia, które pozwala klientowi ją włączyć i odpowiednio rozwidlić logikę. Nie jestem pewien, jak twój klient rozróżniłby różnicę między „404 nie znaleziono danych” a „404 nie znaleziono zasobu”? Nie chcesz, aby ktoś przeglądał użytkownika Z / 9 i kazał klientowi zastanawiać się, jakby żądanie było prawidłowe, ale nie zwrócono żadnych danych.

Wejdź
źródło
1

Dlaczego nie użyć 410? Sugeruje to, że żądany zasób już nie istnieje i oczekuje się, że klient nigdy nie poprosi o ten zasób, w twoim przypadku users/9.

Więcej informacji na temat 410 można znaleźć tutaj: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

Akszat
źródło
2
„oczekuje się, że klient nigdy nie złoży prośby” - a co, jeśli ten użytkownik zostanie utworzony później, twoja odpowiedź sugeruje, że możesz zagwarantować, że ten użytkownik nigdy nie będzie istniał, to bardzo śmiałe założenie i prawdopodobnie błędne
Holly
Co powiedziała Holly. Dodając do tego: nawet jeśli użytkownik istniał i został usunięty, 410 się myli, bo co, jeśli użytkownik zostanie usunięty? (Który to szczególny przypadek tworzenia się później).
Christian Hujer
ponieważ 410 powinno być sytuacją stałą. restapitutorial.com/httpstatuscodes.html
Akostha
1

To smutne, że coś tak prostego i dobrze zdefiniowanego stało się „oparte na opiniach” w tym wątku.

Serwer HTTP zna tylko „encje”, które są abstrakcją dla dowolnej treści, czy to statyczna strona internetowa, lista wyników wyszukiwania, lista innych encji, opis czegoś, plik multimedialny itp.

Oczekuje się, że każdy taki podmiot będzie można zidentyfikować za pomocą unikalnego adresu URL, np

  • / user / 9 - pojedynczy obiekt: ID UŻYTKOWNIKA = 9
  • / users - pojedynczy byt: LISTA wszystkich użytkowników
  • /media/x.mp3 - pojedynczy byt: PLIK multimediów o nazwie x.mp3
  • / search - pojedynczy byt: dynamiczna TREŚĆ oparta na parametrach zapytania

Jeśli serwer znajdzie zasób pod danym adresem URL, nie ma znaczenia, jaka jest jego zawartość - 2G danych, null, {}, [] - tak długo, jak istnieje, będzie to 200. Ale jeśli taka jednostka jest serwer nie jest znany, oczekuje się, że zwróci 404 „Nie znaleziono”.

Jedno zamieszanie wydaje się pochodzić od programistów, którzy uważają, że jeśli aplikacja ma moduł obsługi dla określonego kształtu ścieżki, nie powinien to być błąd. W oczach protokołu HTTP nie ma znaczenia, co wydarzyło się we wnętrzu serwera (tj. Czy router domyślny zareagował, czy moduł obsługi określonego kształtu ścieżki), o ile na serwerze nie ma pasującego obiektu żądany adres URL (żądany plik MP3, strona internetowa, obiekt użytkownika itp.), który zwróciłby prawidłową treść (pustą lub inną), musi to być 404 (lub 410 itd.).

Kolejnym punktem zamieszania wydaje się być „brak danych” i „brak bytu”. Pierwsza dotyczy zawartości bytu, a druga jego istnienia.

Przykład 1:

  • Brak danych: / users zwraca 200 OK, body: [], ponieważ nikt jeszcze się nie zarejestrował
  • Brak encji: / users zwraca 404, ponieważ nie ma ścieżki / użytkowników

Przykład 2:

  • Brak danych: / użytkownik / 9 zwraca wartość 200 OK, treść: {}, ponieważ identyfikator użytkownika = 9 nigdy nie wprowadził swoich danych osobowych
  • Brak encji: / user / 9 zwraca 404, ponieważ nie ma identyfikatora użytkownika = 9

Przykład 3:

  • Brak danych: / search? Name = Joe zwraca 200 OK [], ponieważ nie ma Joe's w DB
  • Brak encji: / search? Name = Joe zwraca 404, ponieważ nie ma ścieżki / wyszukiwania
Darko Maksimovic
źródło
0

404 byłoby bardzo mylące dla każdego klienta, jeśli wrócisz tylko dlatego, że nie ma danych w odpowiedzi.

Dla mnie kod odpowiedzi 200 z pustym ciałem wystarczy, aby zrozumieć, że wszystko jest idealne, ale nie ma danych spełniających wymagania.

ciemna energia
źródło
0

Według Microsoft: Typy akcji kontrolera zwracane w interfejsie API ASP.NET Core , przewiń w dół prawie do dołu, znajdziesz następujący komunikat o 404 odnoszący się do obiektu nie znalezionego w bazie danych. Tutaj sugerują, że 404 jest odpowiedni dla pustych danych.

wprowadź opis zdjęcia tutaj

Bill Roberts
źródło
0

Jak wielu twierdzi, 404 wprowadza w błąd i nie pozwala klientowi na rozróżnienie, jeśli identyfikator URI żądania nie istnieje lub jeśli żądany URI nie może pobrać żądanego zasobu.

Oczekuje się, że status 200 będzie zawierać dane zasobów - więc nie jest to właściwy wybór. Status 204 oznacza coś zupełnie innego i nie powinien być wykorzystywany jako odpowiedź na żądania GET.

Wszystkie pozostałe istniejące statusy nie mają zastosowania z tego czy innego powodu.

Widziałem ten temat w kółko w wielu miejscach. Dla mnie boleśnie oczywiste jest, że aby wyeliminować zamieszanie wokół tematu, potrzebny jest dedykowany status sukcesu. Coś w rodzaju „ 209 - Brak zasobu do wyświetlenia”.

Będzie to status 2xx, ponieważ nie znalezienie identyfikatora nie powinno być uważane za błąd klienta (gdyby klienci wiedzieli wszystko, co znajduje się w bazie danych serwera, nie musieliby o nic pytać serwera, prawda?). Ten dedykowany status rozwiązuje wszystkie problemy omawiane przy użyciu innych statusów.

Jedyne pytanie brzmi: jak sprawić, by RFC zaakceptował to jako standard?

Bar Umberto
źródło