RESTful API design. Co mam zwrócić, jeśli nie ma żadnych wierszy?

50

Obecnie koduję API dla sieci społecznościowej za pomocą Slim Framework. Moje pytanie brzmi: jakie są najlepsze praktyki, gdy nie ma wierszy do zwrócenia w strukturze json?

Powiedzmy, że to wywołanie / v1 / get / movies zwraca 2 wiersze od tabeli nazw filmów:

[
    {"name": "Ghostbusters"},
    {"name": "Indiana Jones"}
]

Ale potem wywołuję / v1 / get / books i nie ma żadnych wierszy w tej tabeli. Czy powinienem po prostu zwrócić pustą strukturę?

[
]

... czy byłby to lepszy komunikat i kod błędu?

[
    "errors": {
        "message": "no matches found",
        "code": 134
    }
]

Która jest lepsza praktyka? (interfejs API będzie używany w aplikacjach na iOS i Androida) Dzięki!

Andres SK
źródło
3
Dla mnie jest to pytanie, czy zero to tak naprawdę kwota.
scarfridge
16
Twój przykład jest zepsuty. Nie możesz mieć obiektów Json ze zduplikowanymi kluczami. To, czego szukasz, to tablica, tj.[{"name": "..."}, {"name":"..."}]
Martin Wickman
@MartinWickman Przepraszam za to, właśnie to naprawiłem.
Andres SK
8
@andufo, właściwie nie zrobiłeś ...
avakar
25
Jeśli twoja aplikacja ma być RESTful, to dlaczego czasownik / metoda „pobiera” część identyfikatora URI punktu końcowego?
user50849

Odpowiedzi:

46

Zwykle zwracam w wyniku liczbę rekordów jako metadane. Nie jestem pewien, czy jest to normalna praktyka REST, ale nie jest to wiele dodatkowych danych i jest bardzo precyzyjna. Zazwyczaj istnieje wiele stronicowania dla wielu usług, niepraktyczne jest zwracanie ogromnego zestawu wyników naraz. Osobiście denerwuje mnie podział na małe zestawy wyników. Jeśli jest pusty, wróć number_of_records : 0i książki jako pusta lista / tablica books : [].

{
    meta: {
        number_of_records : 2,
        records_per_page : 10,
        page : 0
    },
    books : [
        {id:1},
        {id:27}
    ]
}

EDYCJA (kilka lat później): Odpowiedź Martina Wickmana jest znacznie lepsza, oto „krótkie” wyjaśnienie dlaczego.

W przypadku paginacji należy zawsze pamiętać o możliwości zmiany treści lub zamówienia. Np. Przychodzi pierwsze zapytanie, 24 wyniki, zwracane jest pierwsze 10. Następnie dodaje się „nową książkę”, a teraz masz 25 wyników, ale z oryginalnym żądaniem zostałoby zamówione na 10 miejscu. Gdy pierwszy użytkownik zażąda drugiej strony, nie dostanie „nowej książki”. Istnieją sposoby radzenia sobie z takimi problemami, takie jak podanie „id żądania”, który powinien zostać wysłany z następującymi wywołaniami API, a następnie zwrócenie następnej strony ze „starego” zestawu wyników, który należy jakoś zapisać i powiązać z „id żądania”. Alternatywą jest dodanie pola typu „lista wyników zmieniona od pierwszego żądania”.

Zasadniczo, jeśli możesz, spróbuj włożyć dodatkowy wysiłek i unikaj paginacji. Podział na strony to dodatkowy stan, który można mutować, a śledzenie takich zmian jest podatne na błędy, tym bardziej, że zarówno serwer, jak i klient muszą sobie z tym poradzić.

Jeśli masz za dużo danych do przetworzenia na raz , rozważ zwrócenie „listy identyfikatorów” ze wszystkimi wynikami i szczegółami dla części tej listy i podaj wywołania API multi_get / get_by_id_list API dla zasobów.

grizwako
źródło
1
Huh, zastanawiam się, dlaczego ten nie jest tak wysoko oceniany jak drugi. Podoba mi się to bardziej, ponieważ daje zarówno pustą listę (która miała być listą, prawda?), Którą można ślepo iterować bez specjalnych warunków, ale także metadane liczą się jako sposób powiedzenia: „Nie, nie zrobiliśmy tego” t gloss nad błędem, faktycznie było 0 wyników ".
Izkata
1
-1, ponieważ booksparametr jest obiektem, ale „książki” implikują więcej niż jeden, a więcej niż jeden implikuje tablicę. Metadane są fajne i ostatecznie spodziewam się, że zbiór książek będzie tablicą obiektów książkowych; jeśli nie ma książek, daj mi pustą tablicę
Charles Sprayberry
9
Problem polega na tym, że dodanie „liczba_rejestrów” nie dostarcza więcej informacji, tylko dodaje redundancję i zwiększa złożoność. Aby zasygnalizować błąd, zwróć odpowiedni kod http + coś w treści.
Martin Wickman
1
@cspray books to lista, jak wskazała Izkata, moja literówka.
grizwako
2
@MartinWickman Nie chciałem skazić oryginalnej odpowiedzi dodatkowymi metadanymi, ale z mojego doświadczenia wynika, że ​​wiele usług nie zwraca wszystkich danych od razu, ale w sposób „paginowany”.
grizwako
105

Twój przykład jest zepsuty. Nie powinieneś mieć obiektów Json ze zduplikowanymi kluczami. To, czego szukasz, to tablica z obiektami filmowymi, takimi jak to:

 [
    {"name": "movie1"}, 
    {"name": "movie2"}
 ]

To podejście również odpowiada na twoje pytanie. Powinieneś zwrócić pustą tablicę, gdy zapytanie nie pasuje:

[]

Z drugiej strony, jeśli spróbujesz uzyskać konkretny zasób filmu, GET api/movie/34a ten film nie istnieje, zwróć 404 z odpowiednim komunikatem o błędzie (zakodowanym w formacie json) w treści

Martin Wickman
źródło
1
+1 To jest poprawny JSON zgodnie z json_xs.
l0b0
15

Jeśli jest to JSON, naprawdę powinieneś rozważyć zwrócenie tablicy obiektów. Ma to wiele zalet, w tym to, że gdy nie masz żadnych rekordów, jest to pusta tablica.

Więc kiedy masz rekordy, powrócisz:

    [
        {"name": "Ghostbusters"},
        {"name": "Indiana Jones"}
    ]

A jeśli nie masz żadnych zapisów, powrócisz:

    [

    ]
Devdatta Tengshe
źródło
14

Jeśli operacja zostanie wykonana pomyślnie, ale nie ma nic do zwrócenia, na przykład pusta mapa {}lub pusta tablica []Wolałbym odpowiedzieć kodem odpowiedzi 204, oto fragment specyfikacji Definicje kodów stanu HTTP :

Serwer spełnił żądanie, ale nie musi zwracać treści encji i może chcieć zwrócić zaktualizowane metainformacje. Odpowiedź MOŻE zawierać nowe lub zaktualizowane metainformacje w postaci nagłówków encji, które, jeśli są, POWINNY być powiązane z żądanym wariantem.

Jeśli klient jest agentem użytkownika, NIE POWINIEN zmieniać widoku dokumentu w stosunku do tego, który spowodował wysłanie żądania. Ta odpowiedź ma przede wszystkim umożliwić wprowadzanie działań bez powodowania zmiany w aktywnym widoku dokumentu agenta użytkownika, chociaż wszelkie nowe lub zaktualizowane metainformacje POWINNY być stosowane do dokumentu znajdującego się obecnie w aktywnym widoku agenta użytkownika.

Odpowiedź 204 NIE MOŻE zawierać treści komunikatu, a zatem zawsze kończy się pierwszą pustą linią po polach nagłówka.

Zasadniczo zalecam używanie 204 w aplikacjach RESTful przez HTTP, gdy nie ma nic do zwrócenia.

David Sergey
źródło
4
Zgadzam się z komentarzem @ avakar do innej odpowiedzi tutaj. Jeśli klient próbuje uzyskać dostęp do / v1 / get / movies / 1, powinien zwrócić 404, jeśli nie ma żadnych filmów identyfikowanych przez 1. Tylko / v1 / get / movies powinien zwrócić 200, nawet jeśli nie ma filmu. Ale 204 nie jest odpowiednie, ponieważ jest przeznaczone do akcji wejściowych.
imel96
7
Innym problemem związanym z tym rozwiązaniem jest to, że zwykle potrzebuje specjalnego kodu w kliencie: jeśli odpowiedź jest pustą listą, może być analizowana jako JSON, tak jak normalna odpowiedź. Jeśli odpowiedź jest pusta, parser JSON prawdopodobnie narzeka (ponieważ pusty dokument nie jest prawidłowym JSON), więc klient potrzebuje dodatkowego kodu, aby sprawdzić HTTP 204 i pominąć parsowanie.
sleske
7
Uważam, że jest to błędna interpretacja zamiaru 204. Wydaje się, że 204 nie jest przeznaczony do operacji, które oczekiwały treści i nie znalazły żadnej, ale raczej do operacji, które zakończyły się powodzeniem i nie przyniosły zamierzonego zwrotu . Z Wikipedii: „Serwer pomyślnie przetworzył żądanie, ale nie zwraca żadnej treści. Zwykle służy jako odpowiedź na pomyślne żądanie usunięcia”.
XML
10

Sporo pracy wykonano przy tworzeniu znormalizowanego formatu API JSON .

Przestrzeganie zasad zawartych w tej specyfikacji oznacza, że ​​wszystkie zwracane zasoby powinny być skutecznie „kolekcjami” (nawet jeśli uwzględniony jest tylko jeden zasób). Postępowanie w ten sposób oznaczałoby, że Twoje wezwanie do /v1/get/moviespowrotu:

{
    "movies": [
        {"name": "Ghostbusters"},
        {"name": "Indiana Jones"}
    ]
}

Twoje wezwanie do /v1/get/books(które zwraca zero zasobów) zwróci:

{
    "books": []
}
Tim Blair
źródło
5

W twoim konkretnym przykładzie zaleciłbym, aby / v1 / get / books zwrócił HTTP 200 z pustą tablicą.

Jeśli dobrze czytam Twój post, Twój interfejs API zamierza zbierać książki. Mówiąc metaforycznie, masz półkę na książki, stojak na filmy DVD i ewentualnie inne pojemniki, o których tu nie wspomniałeś. Ponieważ zamierzasz zbierać książki, / v1 / get / books to twoja półka na książki. Oznacza to, że istnieje tam ważny zasób - lista książek - które w twoim przykładzie są puste.

Powodem, dla którego nie sugeruję zwracania HTTP 404 w tym przypadku jest to, że półka wciąż tam jest. W tej chwili nie ma na nim żadnych książek, ale nadal jest to półka na książki. Jeśli nie byłby to półka z książkami - jeśli API nie zamierzał na przykład zbierać książek - wtedy HTTP 404 byłby odpowiedni. Ale ponieważ istnieje tam zasób, nie powinieneś sygnalizować, że nie ma takiego zasobu, co robi HTTP 404. Dlatego twierdzę, że 200 z pustą tablicą (oznaczającą kolekcję) jest bardziej odpowiednie.

Powodem, dla którego nie sugeruję zwracania HTTP 204, jest to, że sugerowałoby to, że „Brak treści” jest zwykłym stanem rzeczy: wykonanie tej akcji na tym zasobie zwykle niczego nie zwróci. Dlatego jest zwykle używany jako odpowiedź na żądania DELETE, na przykład: charakter usunięcia ogólnie oznacza, że ​​nie ma nic do zwrócenia. Sprawa jest podobna, gdy jest używana do odpowiadania na żądania za pomocą rodziny nagłówków If-Modified: chciałeś treści tylko, jeśli zasób się zmienił, ale nie zmienił się, więc nie dam ci żadnej treści.

Twierdzę jednak, że dla POBIERANIA pustej, ale poprawnej kolekcji, HTTP 204 nie ma sensu. Jeśli w kolekcji znajdują się elementy, poprawną reprezentacją będzie tablica tych danych. Dlatego, gdy nie ma tam żadnych danych (ale kolekcja jest poprawna), poprawną reprezentacją jest pusta tablica.

The Spooniest
źródło
5

Naprawdę powinieneś zrobić tylko jedną z dwóch rzeczy

Albo Zwróć 200 (OK)kod stanu i pustą tablicę w treści.

Lub Zwróć 204 (NO CONTENT)kod stanu i BRAK odpowiedzi.

Dla mnie opcja 2 wydaje się bardziej poprawna technicznie i zgodna z zasadami REST i HTTP.

Jednak opcja 1 wydaje się bardziej wydajna dla klienta - ponieważ klient nie potrzebuje dodatkowej logiki, aby rozróżnić dwa (statusowe) kody stanu. Ponieważ wie, że zawsze otrzyma tablicę, musi po prostu sprawdzić, czy nie dostał żadnego, jednego lub wielu elementów i odpowiednio go przetworzyć

Vihung
źródło
3

Widziałem oba przypadki w środowiskach produkcyjnych. Wybór zależy od tego, kto będzie korzystał z interfejsu API. Jeśli chcą wiedzieć, dlaczego lista jest pusta lub mieć pewność, że lista jest naprawdę pusta i podczas jej pobierania nie wystąpiły żadne błędy, należy dołączyć obiekt „error”. Jeśli ich to nie obchodzi, zwróć pustą listę. Wybrałbym drugie podejście, ponieważ obejmuje ono więcej potrzeb niż pierwsze.

diabelnie
źródło
3

Pierwszą rzeczą do rozważenia, ponieważ budujesz interfejs API RESTful, jest zwrócenie odpowiedniego kodu odpowiedzi. Bardziej odpowiednim kodem odpowiedzi do komunikowania się, że żądanie przebiegło normalnie, ale żądany zasób nie jest w tej chwili dostępny, jest czcigodny 404.

Jeśli projektujesz interfejs API w taki sposób, aby zawsze zwracał rozsądny kod odpowiedzi, być może nawet nie będziesz musiał zwracać treści, gdy nie znaleziono zasobu. To powiedziawszy, zwrócenie ciała, szczególnie ludzkiego, nie może zranić.

Nie ma tu „najlepszej praktyki”, oba przykłady są arbitralne, wystarczy wybrać jedną i zachować spójność . Programiści nienawidzą niespodzianek. Jeśli /v1/get/moviespowróci, {}gdy nie będzie filmów, spodziewamy /v1/get/actorssię powrotu, {}gdy nie będzie aktorów.

Yannis
źródło
1
Zwrócenie 404 naprawdę jest właściwą rzeczą, ale niestety nikt tak naprawdę nie robi - w tym ja.
RibaldEddie
1
jeśli masz złożone odpowiedzi, a tylko niektóre z nich są puste, zwrócenie wartości 404 wprowadzi użytkownika w błąd.
devnull
5
Nie zgodziłbym się z wiadomością 404. 404 zinterpretowałbym jako „zasób nie istnieje” i martwię się, jeśli coś nie tak z moim adresem URL lub czymkolwiek innym. Gdybym poprosił o listę filmów i dostał 404, pomyślałbym, że w ogóle nie ma zasobu filmowego. „204 Brak treści” może być bardziej odpowiednie.
thorsten müller
8
Ok, „brak ciała” by go zabiło. Ale: „Klasa kodu statusu 4xx jest przeznaczona dla przypadków, w których klient prawdopodobnie się pomylił.”. Ale nie było błędu po stronie klienta. Zatem 404 podaje błędne informacje. Wyślij 204 bez ciała lub powiedz, że jest w porządku i wyślij pustą listę.
thorsten müller
9
Prosisz o listę książek, zwracanie 404 oznaczałoby, że lista nie istnieje, a nie, że jest pusta. Zwrócenie 200 wraz z pustą listą wydaje mi się jedyną rozsądną opcją.
avakar
1

Nie wydaje mi się, żeby odpowiedź była ścisła.

Odpowiedź udzielona przez nirth powinna być najlepsza, w prawdziwym scenariuszu REST. Odpowiedź ciała powinna być pusta, a kod stanu http: 204; zasób istnieje, ale w tym czasie nie ma „treści”: jest pusty.

REST HTTP_Status_Codes

jmmtcarvalho
źródło
1

Polecam ponad 200 pustych tablic, ponieważ upraszcza to obsługę wszystkich klientów interfejsu API. Tablica 200+ oznacza „Zwróciłem wszystkie dane, które tam są”. Zarówno w przypadku kodu dostarczającego dane, jak i kodu przetwarzającego je liczba elementów byłaby nieistotna.

Każdy inny kod stanu musi być odpowiednio udokumentowany i prawidłowo dostarczony przez serwer i odpowiednio przetworzony przez klienta, a wszyscy wiemy, jak prawdopodobne jest to zdarzenie.

Sugerowano zwrócenie statusu 204 + puste ciało. Oznacza to, że zmuszają każdego pojedynczego klienta do stanu procesu 204 poprawnie. Ponadto zmuszasz ich do obsługi odpowiedzi innych niż JSON! Mam nadzieję, że wszyscy zdają sobie sprawę, że tylko dlatego, że żądanie otrzymało odpowiedź, nie oznacza to, że odpowiedź pochodzi z serwera, gdy używany jest protokół HTTP, a jedynie sprawdzenie, czy odpowiedź to JSON, obsługuje wiele z tych przypadków.

gnasher729
źródło
0

Chciałbym „To zależy”.

Jeśli zero jest rozsądnym wynikiem, zwróć pustą listę. Na przykład, jeśli chcesz, aby wszyscy pracownicy nazywali się „bob”, a „brak” to całkiem rozsądny wynik. Jeśli nie jest to oczekiwany wynik, zwróć błąd. Na przykład uzyskanie historycznej listy adresów dla osoby, którą zatrudniasz. Muszą oni gdzieś mieszkać, więc żaden wynik nie jest prawdopodobnie błędem, a nie zwykłym stanem.

Jestem pewien, że możesz spierać się ze specyfiką mojego przykładu, ale masz pomysł ...

JohnB
źródło
0
  • Po pierwsze, posiadanie getadresu URL nie jest RESTful, metoda GET implikuje metodę HTTP.
  • Jeśli żądasz kolekcji, np. GET api/moviesZwróć a 200 OKz pustą tablicą [].
  • Jeśli żądasz określonego filmu, takiego jak GET api/movies/1(gdzie 1jest identyfikator) i nie istnieje, zwróć a 404 Not Found.

Dlaczego? Żądasz zasobów . Gdy żądasz kolekcji, sam zasób (kolekcja) istnieje. Dlatego 404jest źle. Ale jeśli poprosisz o konkretny film i nie istnieje, żądany zasób nie istnieje i musisz go zwrócić 404.

felixfbecker
źródło
-2

Jeśli zwracasz JSON, lepiej zawsze zwracać liczbę i komunikat o błędzie i być może wartość logiczną wskazującą, czy wystąpił błąd, czy nie, to są moje trzy standardowe meta wartości zwracane z każdą listą wierszy.

sino
źródło