API RESTful reprezentuje brak rzeczy

18

Wyobraź sobie interfejs API pozwalający ustalić, czy dana osoba wybrała zwierzę duchowe. Mogą mieć tylko zero lub jeden duchowy zwierzak.

Obecnie:

/person/{id}/selectedSpiritAnimal

po wybraniu zwierzę zwraca http 200 i {selectedAnimal:mole}

ale gdy nie mają wyboru, zwraca http 404.

To sprawia, że ​​moje zwierzę duchowe jest nieszczęśliwe, ponieważ reprezentujemy ważny problem związany z domeną - nie wybrałem jeszcze zwierzęcia duchowego - jako błąd HTTP.

Ponadto, jako firma - erm Sprit-Animal-Hampers-R-us - chcemy wiedzieć, kiedy ktoś nie ma wyboru, abyśmy mogli go o to poprosić.

Jaka jest lepsza odpowiedź tutaj:

HTTP 200 i {selectedAnimal:null}

lub nawet bardziej wyraźnie

HTTP 200 i {selectedAnimal:null, spiritAnimalSelected: false}

A może lepiej jest zwrócić 404? Ponieważ podobnie jak this image has not yet been uploadedpodczas oglądania obrazu w trybie online byłoby 404. this person has not selected a spirit animalmoże być 404


To pytanie zostało zaproponowane jako duplikat, ale pytanie to dotyczy innego ważnego adresu URL wymaganego, gdy aplikacja została skonfigurowana tak, aby nie zezwalać na zmianę reprezentowaną przez adres URL.

Podczas gdy tutaj patrzę, jak reprezentuje się zasób, w którym brak zasobu jest znaczący. Oznacza to, że klient może zażądać adresu URL, a odpowiedź brzmi: pomyślnie zażądałeś zasobu, który reprezentuje brak jakiejś rzeczy.

Więc to nie jest „logika biznesowa”, ale raczej okoliczność, w której brak czegoś ma znaczenie (może być tak, jak wielu moich kolegów twierdzi, że 404 jest nadal poprawne), ale nie jestem pewien, jak odwzorować to na spec.


Bardzo trudno jest wybrać odpowiedź. Wielokrotnie zmieniałem zdanie na temat rozmowy tutaj i tej trwającej w pracy.

Rzeczą, która mnie tu rozlicza, jest to, że specyfikacja mówi, że 4xx jest wtedy, gdy klient się pomylił . W tym przypadku klientowi powiedziano, aby oczekiwał odpowiedzi z wybranego adresu URLSpiritAnimal, więc nie popełnił błędu.

Konsensus wśród moich kolegów jest taki, że jest to objaw złego projektu API

Prawdopodobnie byłoby lepiej, gdybyśmy po prostu poprosili / person / {id}, a to zwróci zestaw relacji linków dla tej osoby ... to jeśli nie otrzymasz linku / selectedSpiritAnimal (gdy dana osoba nie ma wyboru), ale ty nazwij to mimo to, wtedy 404 ma sens. Lub że implementujesz częściowe odpowiedzi i pozwalasz / person / {id} zwrócić pełniejszy dokument, chyba że klient zażąda podzbioru danych

Paul D'Ambra
źródło
5
Nie wyjaśniłeś, dlaczego Twoim zdaniem zwracanie wartości 404 jest problemem. Z punktu widzenia domeny nie wiesz, czy otrzymałeś wartość 404, czy 200, co jest abstrakcyjne dla warstwy klienta.
Vincent Savard
14
a może lepiej zwrócić 204 brak treści?
Paul D'Ambra,
6
Przypuszczam, że moje obawy związane z 404 to ujednoznacznienie zmiany konfiguracji, która wprowadza zły szablon adresu URL od osoby bez zwierzęcia-ducha
Paul D'Ambra
2
@ PaulD'Ambra Za to, co jest warte, nie użyłbym żadnej treści. Od czasów XmlHttpRequest nie widziałem tak obsługiwanych w interfejsie kodów HTTP w JavaScript. Ale to z pewnością jest ważne.
Brandon Arnold,

Odpowiedzi:

24

Kody HTTP 4xx prawdopodobnie nie są właściwym wyborem dla tego scenariusza. Oświadczasz, że posiadanie zerowych zwierząt-duchów jest prawidłowym stanem, a trasa API person/{id}/selectedSpiritAnimaluwzględnia, czy dana osoba je idma, czy nie.

Odpowiedzi HTTP 4xx są zarezerwowane na sytuację, w której klient zrobił coś nieprawidłowego w żądaniu (patrz archiwum oryginalnej specyfikacji w3 ). Ale klient składa prawidłowe żądanie, niezależnie od tego, czy dana osoba idma zwierzę duchowe.

Dlatego skłaniam się ku drugim rozwiązaniom, używając odpowiednio sformatowanej treści JSON w odpowiedzi i kodu HTTP 2xx.

Teraz, jeśli otrzymasz taką prośbę i okaże się, że osoba idnie istnieje, kod 4xx ma większy sens.

Brandon Arnold
źródło
17
„Odpowiedzi HTTP 4xx są zarezerwowane dla sytuacji, gdy klient zrobił coś nieprawidłowego w żądaniu”. Dokonując autorytatywnych oświadczeń na temat specyfikacji, należy odwołać się do faktycznej specyfikacji HTTP, a nie (a) zbudowanej na niej struktury lub (b) losowego wpisu na blogu.
Eric Stein,
5
@EricStein Czułem się trochę leniwy; dzięki za krytykę. Zaktualizowano
Brandon Arnold,
1
Wydaje mi się, że tutaj link RFC jest trochę sprzeczny. Z jednej strony mówi część 4xx cases in which the client seems to have erred, ale z drugiej strony 404 mówi wprost The server has not found anything matching the Request-URI. Możesz rozważyć żądanie czegoś, co nie jest błędem klienta. Rozumiem, że osoba nie istnieje, a nie ma podpory pomocniczej, ale może to być wskazane jako komunikat odpowiedzi. 204 wydaje się również istotne, ponieważ jednostka istnieje, ale nie ma treści dla pod własności.
shortstuffsushi,
1
Klient zrobił coś złego; zwrócił się do wybranego zwierzęcia duchowego dla użytkownika, gdy nie istnieje. Dokładnie to oznacza 404 ... poprosiłeś o coś, czego nie ma.
Andy,
5
Kiedy moja przeglądarka wysyła zapytanie do softwareengineering.stackexchange.com/questions/unicorn , jest to prawidłowe żądanie, a mimo to wciąż otrzymuję 404 (zdarza się, że nie ma pytania o identyfikatorze „jednorożec”).
user253751
24

Pozwól, że przedstawię ci model dojrzałości Richardsona .

Twój problem polega na tym, że reprezentujesz dwa zasoby jako jeden, w którym tak naprawdę powinieneś mieć dwa zasoby, których relacje są wskazane przez hipermedia. Używanie hipermedii do opisywania związków to wspaniały poziom 3 Odpoczynku.

Twoja osoba powinna żyć pod URI, /person/{id}a zwierzę powinno żyć /spiritanimal/{id}. Osoba powinna wskazać, że ma zwierzęcia duchowego, używając linku do zwierzęcia.

Wyobraźmy sobie osobę o imieniu Bob, która ma identyfikator 123 i zwierzęce duchy Jednorożca.

GET /person/123

wróciłby;

{
  "name": "Bob",
  "links": [
    {
      "rel": "spiritanimal",
      "uri": "/spiritanimals/789"
    }
  ]
}

Teraz każdy, kto czyta osobę 123, będzie wiedział, że ma duchowe zwierzę i ma identyfikator URI, w którym może uzyskać więcej informacji na jego temat.

GET /spitiranimal/789

może wrócić

{
  "type": "Unicorn"
}

Teraz wyobraźmy sobie osobę o imieniu Fred, która ma identyfikator 456 i nie ma zwierzęcia-ducha.

GET /person/456

wróciłby;

{
  "name": "Fred",
  "links": [
  ]
}

Teraz każdy, kto czyta osobę 456, będzie wiedział, że nie ma duchowego zwierzęcia, ponieważ nie ma linku. Nie ma potrzeby używania żadnego kodu stanu HTTP, który reprezentuje brak relacji.

Qwerky
źródło
1
Właśnie dodawałem do pytania, że ​​biorąc pod uwagę możliwość zmiany interfejsu API, pewien poziom HATEOAS byłby prawdopodobnie właściwym rozwiązaniem. To świetny przykład
Paul D'Ambra
1

To jest odpowiedni adres URL do pozyskiwania duchowych zwierząt; dlatego błąd 404 jest nieodpowiedni. 404 oznacza problem techniczny, a nie logiczny.

Właściwym rozwiązaniem jest zwrócenie http 200 i {"selectedAnimal": null}

Powinieneś mieć oddzielna WebMethod /person/{id}/hasSelectedSpiritAnimalktóra zwraca {"isSpiritAnimalSelected": false}. Za kulisami może, ale nie musi, wykonywać tych samych wywołań metod, po prostu zwraca wartość false, jeśli jest pusta, ale to zależy od decyzji, a nie od konsumującego kodu.

Lepiej jest unikać łączenia oddzielnych zapytań w jedną metodę internetową bez ważnego powodu, nawet jeśli zapytania są ściśle powiązane.

TheCatWhisperer
źródło
1
Czy możesz wyjaśnić, dlaczego Twoim zdaniem jest to właściwe rozwiązanie? Nie mam sensu zwracać danych, gdy nie ma danych do zwrócenia. Zgadzam się z tobą, że 404 nie ma sensu, ponieważ adres URL jest w porządku, więc 204 wydaje mi się najbardziej sensowny.
Vincent Savard
2
Kody stanu @VincentSavard są trudniejsze w obsłudze niż odpowiedzi JSON i są bardziej odpowiednie w przypadku problemów technicznych niż przekazywania informacji o domenie.
TheCatWhisperer
2
204: Brak treści z pustym ciałem. Wszystko jest jasne i zrozumiałe. Nie trzeba się kłócić dalej. Podsumowując, gdy nie ma wyraźnego wzorca projektowego, SE powinien wybrać jeden, dopasować go do swoich potrzeb i dostarczyć dokumentację. Zwrócenie ciała z odpowiedzią 204 nie jest konsekwencją.
formixian
2
@formixian ... Gdybyś tworzył API Bjson / Tcp zamiast JSON / http, co byś zwrócił w tej sprawie? Wydaje mi się, że poleganie na kodach HTTP niepotrzebnie wiąże wyniki interfejsu API z jego implementacją HTTP.
TheCatWhisperer
2
@VincentSavard When you said "the spirit animal is not currently on file", it seemed quite similar to me to "there is no content"Brak treści oznacza brak treści . tzn .: zwraca „”. Te dwa wcale nie są podobne. „zwierzę duchowe nie jest aktualnie przechowywane” różni się bardzo od „”.
Shane
1

To, co reprezentuje twój punkt końcowy, to nie tylko zwierzę; to zwierzę lub jego brak. Jest to wartość najlepiej reprezentowana przez Optional/ Maybe/ Nullable/ itp.

Tak więc prawidłowe wartości (jak w przypadku 200 OK) mogą być:

  • {'animal': <some animal>, 'selected': true}
  • {'animal': null, 'selected': false}

Mogę sobie wyobrazić, że ta DELETEmetoda, zastosowana do punktu końcowego, może ustawić 'selected'się falseponownie, czyli rozbroić wybrane zwierzę.

Możesz oczywiście upuścić 'selected'klucz tutaj, jest pokazany tylko dla jasności; ciąg vs nulljest wystarczający do rozróżnienia.

9000
źródło
0

Powinieneś użyć 404.

Kod stanu 404 (Nie znaleziono) wskazuje, że serwer źródłowy nie znalazł bieżącej reprezentacji zasobu docelowego

RFC7231

HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 25 Sep 2017 00:00:00 GMT

"mole"

HTTP/1.1 404 Not Found
Content-Type: text/plain
Date: Mon, 25 Sep 2017 00:00:00 GMT

this person has not selected a spirit animal

Ponieważ tworzysz interfejs programistyczny, a nie ludzki, tekst 404 jest opcjonalny.

Wolę to, ponieważ najbardziej preferuję standardowe protokoły. HTTP ma sposób reprezentowania nieistnienia i właśnie tego bym użył.

EDYCJA: Załóżmy, że dodałeś funkcję, w której użytkownicy mogą decydować, czy udostępnić swoje duchowe zwierzęta, a ktoś tego nie udostępnił. Czy zwrócisz 200 OK null, czy 200 OK "Unauthorized? A może użyłbyś standardowego 401 Nieautoryzowanego / 403 Zabronionego? Wydaje się to wprost analogiczne do tego, aby nie wybierać jednego.


Alternatywnie, jeśli chcesz użyć 200 OK + JSON, powinieneś wrócić null.

HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 25 Sep 2017 00:00:00 GMT

"mole"

HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 25 Sep 2017 00:00:00 GMT

null

Utrzymuj wszystko w czystości. Utwórz więcej opakowań tylko w razie potrzeby.

Paul Draper
źródło
2
Dane zostały jednak znalezione. Zdarza się, że dane są null(nie mają wartości). Różni się to od tego, że klient żąda czegoś, dla którego nie istnieje gniazdo do przechowywania wartości. Nie sugerowałbyś wrzucania AttributeErrorPythona, gdy zdarza się, że jest to wartość None; sprawiłoby to, że interfejs byłby niepraktyczny i niepotrzebnie trudny w obsłudze. Bardziej pragmatycznie, wiele interfejsów API klienta HTTP może przekonwertować 404 na standardowy mechanizm obsługi błędów języka (kod błędu, wyjątek, typ błędu), co oznacza, że ​​będziesz musiał wyeliminować dodatkowy błąd.
jpmc26,
@Paul Draper Przewiń nieco w górę w specyfikacji, zobaczysz, że zawiera ona ogólne stwierdzenie dotyczące HTTP 4xx: „Klasa kodu statusu 4xx jest przeznaczona dla przypadków, w których klient prawdopodobnie się pomylił”. Operacja wyraźnie stwierdziła, że ​​brak zwierzęcia-ducha jest prawidłowym stanem, za który API powinien się liczyć. tools.ietf.org/html/rfc7231#section-6.5
Brandon Arnold
Jak zatem odróżnić wskazanie nieistnienia żądanego zasobu (tzn. Nie wybrano zwierzęcia) od klienta żądającego nieprawidłowej ścieżki (tj. /person/{id}/selectedSpritAnimalObserwować literówkę Sprit).
sampathsris,
1
Niezależnie od intencji, 404 ściśle oznacza, że ​​nie znaleziono zasobu. Jeśli selectedSpiritAnimal jest zasobem i żaden z nich nie istnieje, nie ma nic do pobrania, a 404 jest odpowiednie. Jeśli jednak klient chce wiedzieć, czy zwierzę duchowe zostało wybrane, serwer powinien udostępnić inny zasób, /person/{id}/isSpiritAnimalSelectedktóry poinformuje klienta, czy zwierzę zostało wybrane. Wydaje mi się, że ten sam klasyczny przykład testowania, czy plik istnieje przed jego odczytaniem. Wystarczy przeczytać plik i obsłużyć błąd ENOENT, jeśli zostanie zgłoszony.
aaaaaa
1
@PaulDraper Nie chodzi o to, czy dany zasób istnieje. Chodzi o to, że kody HTTP 4xx są przeznaczone, gdy osoba dzwoniąca nieprawidłowo używa interfejsu API. Op stwierdza, że /person/{id}/selectedSpiritAnimalnależy go używać, nawet jeśli zwierzę duchowe nie istnieje. Oznacza to, że HTTP 4xx jest niepoprawny, jeśli zostanie użyty w takim przypadku. Op również poprawnie stwierdza, że ​​może to być zapach złego projektu API.
Brandon Arnold,