Tworzę usługę REST API dla dużego serwisu społecznościowego, w którym jestem zaangażowany. Na razie działa świetnie. Mogę wydać GET
, POST
, PUT
i DELETE
wnioski do adresów URL obiektu i wpływają na moje dane. Jednak te dane są stronicowane (ograniczone do 30 wyników na raz).
Jaki byłby jednak najlepszy sposób RESTful na uzyskanie całkowitej liczby powiedzmy członków za pośrednictwem mojego interfejsu API?
Obecnie wysyłam żądania do struktury adresu URL, takiej jak:
- / api / members - zwraca listę członków (30 naraz, jak wspomniano powyżej)
- / api / Members / 1 - wpływa na pojedynczego członka, w zależności od użytej metody żądania
Moje pytanie brzmi: w jaki sposób użyłbym podobnej struktury adresu URL, aby uzyskać całkowitą liczbę członków w mojej aplikacji? Oczywiście żądanie tylko id
pola (podobnie jak w Graph API Facebooka) i zliczanie wyników byłoby nieskuteczne, biorąc pod uwagę, że zwrócony zostałby tylko wycinek 30 wyników.
źródło
Odpowiedzi:
Podczas gdy odpowiedź do / API / users jest stronicowana i zwraca tylko 30 rekordów, nic nie stoi na przeszkodzie, aby w odpowiedzi uwzględnić również całkowitą liczbę rekordów i inne istotne informacje, takie jak rozmiar strony, numer strony / przesunięcie itp. .
Interfejs API StackOverflow jest dobrym przykładem tego samego projektu. Oto dokumentacja dotycząca metody Users - https://api.stackexchange.com/docs/users
źródło
Wolę używać nagłówków HTTP do tego rodzaju informacji kontekstowych.
Dla całkowitej liczby elementów używam
X-total-count
nagłówka.W przypadku linków do następnej, poprzedniej strony itp. Używam
Link
nagłówka http :http://www.w3.org/wiki/LinkHeader
Github robi to w ten sam sposób: https://developer.github.com/v3/#pagination
Moim zdaniem jest bardziej przejrzysty, ponieważ można go używać również przy zwracaniu treści, które nie obsługują hiperłączy (np. Pliki binarne, obrazy).
źródło
X-
.Ostatnio przeprowadziłem obszerne badania nad tym i innymi pytaniami związanymi ze stronicowaniem REST i pomyślałem, że dodanie niektórych moich ustaleń tutaj jest konstruktywne. Rozszerzam nieco pytanie, aby uwzględnić przemyślenia na temat stronicowania, a także liczbę, ponieważ są one ściśle powiązane.
Nagłówki
Metadane stronicowania są dołączane do odpowiedzi w postaci nagłówków odpowiedzi. Dużą zaletą tego podejścia jest to, że sam ładunek odpowiedzi jest po prostu tym, o co prosił żądający danych. Ułatwienie przetwarzania odpowiedzi klientom, którzy nie są zainteresowani informacjami dotyczącymi stronicowania.
Istnieje wiele (standardowych i niestandardowych) nagłówków używanych w środowisku naturalnym do zwracania informacji związanych ze stronicowaniem, w tym całkowitej liczby.
Całkowita liczba X
Jest to używane w niektórych interfejsach API , które znalazłem na wolności. Istnieją również pakiety NPM do dodania obsługi tego nagłówka np. Do Loopback. Niektóre artykuły zalecają również ustawienie tego nagłówka.
Jest często używany w połączeniu z
Link
nagłówkiem, co jest całkiem dobrym rozwiązaniem do stronicowania, ale brakuje mu informacji o całkowitej liczbie.Połączyć
Czuję, czytając wiele na ten temat, że ogólny konsensus jest użycie
Link
nagłówka , aby zapewnić stronicowania linki do klientów korzystającychrel=next
,rel=previous
itd. Problemem jest to, że brakuje informacji o tym, jak wiele łącznych zapisów istnieją, co jest dlaczego wiele interfejsów API łączy to zX-Total-Count
nagłówkiem.Alternatywnie, niektóre API, np. Standard JsonApi , używają
Link
formatu, ale dodają informacje w kopercie odpowiedzi zamiast do nagłówka. Upraszcza to dostęp do metadanych (i tworzy miejsce do dodawania informacji o całkowitym zliczeniu) kosztem rosnącej złożoności dostępu do samych danych (poprzez dodanie koperty).Zakres zawartości
Promowany przez artykuł na blogu zatytułowany Nagłówek zakresu, wybieram Cię (do paginacji)! . Autor przedstawia argumenty przemawiające za używaniem nagłówków
Range
iContent-Range
do paginacji. Kiedy uważnie zapoznać się z RFC na te nagłówki, okazuje się, że rozszerzenie ich znaczenie poza zakresy bajtów faktycznie zablokowane przez RFC i jest wyraźnie dozwolone. Użyty w kontekścieitems
zamiastbytes
, nagłówek Range w rzeczywistości umożliwia nam zarówno zażądanie określonego zakresu elementów, jak i wskazanie, do jakiego zakresu całkowitego wyniku odnoszą się elementy odpowiedzi. Ten nagłówek daje również świetny sposób na pokazanie całkowitej liczby. I jest to prawdziwy standard, który przeważnie odwzorowuje stronicowanie jeden do jednego. Jest również używany na wolności .Koperta
Wiele interfejsów API, w tym ten z naszej ulubionej witryny z pytaniami i odpowiedziami, wykorzystuje kopertę , otokę wokół danych, która służy do dodawania metainformacji o danych. Ponadto standardy OData i JsonApi używają obwiedni odpowiedzi.
Dużą wadą tego (imho) jest to, że przetwarzanie danych odpowiedzi staje się bardziej złożone, ponieważ rzeczywiste dane muszą znajdować się gdzieś w kopercie. Istnieje również wiele różnych formatów tej koperty i musisz użyć właściwego. Jest to wymowne, że obwiednie odpowiedzi z OData i JsonApi są bardzo różne, z mieszaniem OData w metadanych w wielu punktach odpowiedzi.
Oddzielny punkt końcowy
Myślę, że zostało to wystarczająco uwzględnione w innych odpowiedziach. Nie badałem tak dużo, ponieważ zgadzam się z komentarzami, że jest to mylące, ponieważ masz teraz wiele typów punktów końcowych. Myślę, że najładniej jest, jeśli każdy punkt końcowy reprezentuje (zbiór) zasobów.
Dalsze przemyślenia
Musimy nie tylko przekazywać metadane stronicowania związane z odpowiedzią, ale także pozwolić klientowi zażądać określonych stron / zakresów. Warto również przyjrzeć się temu aspektowi, aby uzyskać spójne rozwiązanie. Tutaj również możemy użyć nagłówków (
Range
nagłówek wydaje się bardzo odpowiedni) lub innych mechanizmów, takich jak parametry zapytania. Niektórzy ludzie opowiadają traktowanie stron wyników w postaci oddzielnych środków, które mogą mieć sens w niektórych przypadkach zastosowania (np/books/231/pages/52
. Skończyło się wybierając dziką gamę najczęściej używanych parametrów żądania takich jakpagesize
,page[size]
ilimit
etc oprócz wspieraniaRange
nagłówek (i jak żądanie parametru także).źródło
Range
nagłówkiem, ale nie mogłem znaleźć wystarczających dowodów na to, że używanie czegokolwiek innego niżbytes
typ zakresu jest prawidłowe.acceptable-ranges = 1#range-unit | "none"
Myślę, że to sformułowanie wyraźnie pozostawia miejsce na inne jednostki zakresu niżbytes
, chociaż sama specyfikacja tylko definiujebytes
.Alternatywa, gdy nie potrzebujesz rzeczywistych przedmiotów
Odpowiedź Franci Penov jest z pewnością najlepszym rozwiązaniem, dlatego zawsze zwracasz elementy wraz ze wszystkimi dodatkowymi metadanymi dotyczącymi żądanych podmiotów. Tak to powinno być zrobione.
ale czasami zwracanie wszystkich danych nie ma sensu, ponieważ możesz ich wcale nie potrzebować. Może wszystko, czego potrzebujesz, to metadane dotyczące żądanego zasobu. Na przykład całkowita liczba lub liczba stron lub coś innego. W takim przypadku zawsze możesz poprosić o zapytanie URL, aby Twoja usługa nie zwracała elementów, a jedynie metadane, takie jak:
lub coś podobnego ...
źródło
metaonly
czyincludeitems
nie jest.Możesz zwrócić licznik jako niestandardowy nagłówek HTTP w odpowiedzi na żądanie HEAD. W ten sposób, jeśli klient chce tylko zliczenia, nie musisz zwracać rzeczywistej listy i nie ma potrzeby stosowania dodatkowego adresu URL.
(Lub, jeśli jesteś w kontrolowanym środowisku od punktu końcowego do punktu końcowego, możesz użyć niestandardowego czasownika HTTP, takiego jak COUNT).
źródło
Zalecałbym dodanie nagłówków na to samo, na przykład:
Po szczegóły patrz:
https://github.com/adnan-kamili/rest-api-response-format
Dla pliku swagger:
https://github.com/adnan-kamili/swagger-response-template
źródło
Od „X -” - prefiks został wycofany. (patrz: https://tools.ietf.org/html/rfc6648 )
Okazało się, że „Akceptuj-zakresy” to najlepszy sposób na zmapowanie zakresu paginacji: https://tools.ietf.org/html/rfc7233#section-2.3 Ponieważ „Jednostki zakresu” mogą mieć wartość „bajty” lub „ znak". Obie nie reprezentują niestandardowego typu danych. (patrz: https://tools.ietf.org/html/rfc7233#section-4.2 ) Niemniej jednak stwierdza się, że
Co oznacza: używanie niestandardowych jednostek zakresu nie jest sprzeczne z protokołem, ale MOŻE zostać zignorowane.
W ten sposób musielibyśmy ustawić Accept-Ranges na „członków” lub jakikolwiek typ jednostki dystansowej, jakiego byśmy się spodziewali. Ponadto ustaw zakres zawartości na bieżący zakres. (patrz: https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.12 )
Tak czy inaczej, trzymałbym się zalecenia RFC7233 ( https://tools.ietf.org/html/rfc7233#page-8 ), aby wysłać 206 zamiast 200:
W rezultacie otrzymalibyśmy następujące pola nagłówka HTTP:
W przypadku częściowej zawartości:
Pełna treść:
źródło
Wydaje się, że najłatwiej jest po prostu dodać plik
i zwróć całkowitą liczbę członków
źródło
A co z nowym punktem końcowym> / api / members / count, który po prostu wywołuje Members.Count () i zwraca wynik
źródło
members
kolekcja może zostać utworzona przez żądanie POST do/api
, zostanie/api/members/count
również utworzona jako efekt uboczny, czy też muszę wykonać jawne żądanie POST, aby ją utworzyć przed zażądaniem? :-)Czasami frameworki (takie jak $ resource / AngularJS) wymagają tablicy jako wyniku zapytania i tak naprawdę nie możesz mieć odpowiedzi, tak jak
{count:10,items:[...]}
w tym przypadku przechowuję „count” w responseHeaders.PS Właściwie możesz to zrobić za pomocą $ resource / AngularJS, ale wymaga to pewnych poprawek.
źródło
isArray: false|true
Możesz rozważyć
counts
jako zasób. Adres URL miałby wtedy postać:źródło
Żądając danych podzielonych na strony, znasz (poprzez jawną wartość parametru rozmiaru strony lub domyślną wartość rozmiaru strony) rozmiar strony, więc wiesz, czy otrzymałeś wszystkie dane w odpowiedzi, czy nie. Kiedy w odpowiedzi jest mniej danych niż rozmiar strony, otrzymujesz pełne dane. Po zwróceniu pełnej strony musisz ponownie poprosić o kolejną stronę.
Wolę mieć oddzielny punkt końcowy dla count (lub ten sam punkt końcowy z parametrem countOnly). Ponieważ możesz przygotować użytkownika końcowego na długi / czasochłonny proces, wyświetlając odpowiednio zainicjowany pasek postępu.
Jeśli chcesz zwrócić rozmiar danych w każdej odpowiedzi, powinieneś również podać pageSize i przesunięcie. Szczerze mówiąc, najlepszym sposobem jest również powtórzenie filtrów żądań. Ale odpowiedź stała się bardzo złożona. Dlatego wolę dedykowany punkt końcowy, aby zwracać liczbę.
Moje połączenie, wolę parametr countOnly do istniejącego punktu końcowego. Tak więc, jeśli zostanie określony, odpowiedź zawiera tylko metadane.
punkt końcowy? filtr = wartość
endpoint? filter = value & countOnly = true
źródło