Paging w kolekcji Rest


134

Jestem zainteresowany udostępnieniem bezpośredniego interfejsu REST do kolekcji dokumentów JSON (pomyśl o CouchDB lub Persevere ). Problem, z którym się spotykam, polega na tym, jak obsłużyć GEToperację na katalogu głównym kolekcji, jeśli kolekcja jest duża.

Jako przykład udaję, że ujawniam Questionstabelę StackOverflow, w której każdy wiersz jest ujawniony jako dokument (niekoniecznie musi istnieć taka tabela, tylko konkretny przykład sporej kolekcji „dokumentów”). Kolekcja będzie udostępniona na /db/questionsze zwykłymi CRUD API GET /db/questions/XXX, PUT /db/questions/XXX, POST /db/questionsjest w grze. Standardowym sposobem uzyskania całej kolekcji jest, GET /db/questionsale jeśli naiwnie zrzuci to każdy wiersz jako obiekt JSON, otrzymasz dość spore pobieranie i dużo pracy ze strony serwera.

Rozwiązaniem jest oczywiście stronicowanie. Dojo rozwiązało ten problem w swoim JsonRestStore za pomocą sprytnego rozszerzenia zgodnego z RFC2616, używającego Rangenagłówka z niestandardową jednostką zasięgu items. Wynikiem jest a, 206 Partial Contentktóry zwraca tylko żądany zakres. Zaletą tego podejścia w porównaniu z parametrem zapytania jest to, że pozostawia on ciąg zapytania dla ... zapytań (np. GET /db/questions/?score>200Lub coś takiego, i tak, które byłyby zakodowane %3E).

To podejście całkowicie pokrywa pożądane przeze mnie zachowanie. Problem polega na tym, że RFC 2616 określa, że ​​w odpowiedzi 206 (moje wyróżnienie):

Żądanie muszą obejmować pole nagłówka Range ( rozdział 14.35 ) wskazując pożądany zakres i mogą mieć włączone pole nagłówka If-Range ( rozdział 14.27 ) do przeprowadzenia żądania warunkowe.

Ma to sens w kontekście standardowego użycia nagłówka, ale stanowi problem, ponieważ chciałbym, aby odpowiedź 206 była domyślna do obsługi naiwnych klientów / przypadkowych ludzi.

Szczegółowo przejrzałem RFC, szukając rozwiązania, ale nie byłem zadowolony z moich rozwiązań i jestem zainteresowany podejściem SO do problemu.

Pomysły, które miałem:

  • Wróć 200z Content-Rangenagłówkiem! - Nie sądzę, żeby to było złe, ale wolałbym bardziej oczywistym wskaźnikiem, że odpowiedź jest tylko Częściową Treścią.
  • Powrót400 Range Required - nie ma specjalnego kodu odpowiedzi 400 dla wymaganych nagłówków, więc domyślny błąd musi być używany i odczytywany ręcznie. Utrudnia to również eksplorację za pomocą przeglądarki internetowej (lub innego klienta, takiego jak Resty).
  • Użyj parametru zapytania - podejście standardowe, ale mam nadzieję, że zezwolę na zapytania a la Wytrwałość, a to przecina przestrzeń nazw zapytania.
  • Po prostu wróć 206! - Myślę, że większość klientów by się nie wystraszyła, ale wolałbym nie występować przeciwko MUST w RFC
  • Rozszerz specyfikację! Return266 Partial Content - zachowuje się dokładnie jak 206, ale jest odpowiedzią na żądanie, które NIE MOŻE zawierać Rangenagłówka. Wydaje mi się, że 266 jest na tyle wysokie, że nie powinienem napotkać problemów z kolizją i ma to dla mnie sens, ale nie jestem pewien, czy jest to uważane za tabu, czy nie.

Myślę, że jest to dość powszechny problem i chciałbym zobaczyć, jak robi się to w sposób de facto, więc ani ja, ani ktoś inny nie wymyślamy na nowo koła.

Jaki jest najlepszy sposób ujawnienia pełnej kolekcji za pośrednictwem protokołu HTTP, gdy kolekcja jest duża?


21
Wow, to dobry przykład pytania, na które już wcześniej przemyślano poważnie.
Heiko Rupp


1
Jeśli chodzi o podejście Dojo do używania nagłówka Range, chociaż Accept-Ranges pozwala na rozszerzenie, z tego, co mogę powiedzieć, EBNF for Range nie: tools.ietf.org/html/rfc2616#section-14.35.2 . Specyfikacja wskazuje, Range = "Range" ":" ranges-specifiergdzie ta ostatnia w tools.ietf.org/html/rfc2616#section-14.35.1 jest opisana jedynie jako „specyfikator-zakresów bajtów”, który musi zaczynać się od „jednostki-bajtów”, która jest zdefiniowana jako ciąg znaków „bajty ”.
Brett Zamir

2
Content-RangeNagłówek odnosi się do ciała (może być używany z prośbą podczas przesyłania dużych plików itp, lub do odpowiedzi podczas pobierania). RangeHeader służy do żądania określonego zakresu. Należy odpowiedzieć, 206kiedy Rangenagłówek został zawarty w żądaniu. Jeśli tak nie jest, odpowiedź może nadal zawierać Content-Rangenagłówek, ale kod odpowiedzi powinien być 200. Ten nagłówek wydaje się być idealny do stronicowania.
Stijn de Witt

Ale sam RFC 2616 mówi, że „implementacje HTTP / 1.1 MOGĄ ignorować zakresy określone przy użyciu innych jednostek”. Czy więc dobrą praktyką jest używanie nagłówków zakresów do paginacji? bo może to zagrozić interoperacyjności.
chetan choulwar,

Odpowiedzi:


23

Mam przeczucie, że rozszerzenia zakresu HTTP nie są zaprojektowane do twojego przypadku użycia, więc nie powinieneś próbować. Częściowa odpowiedź oznacza 206i 206należy ją wysłać tylko wtedy, gdy klient o to poprosił.

Możesz rozważyć inne podejście, takie jak użycie w Atom (gdzie reprezentacja według projektu może być częściowa i jest zwracana ze statusem 200i potencjalnie linkami stronicowania). Zobacz RFC 4287 i RFC 5005 .


14
Wykorzystanie Dojo jest całkowicie zgodne ze specyfikacją. Jeśli serwer nie rozumie itemsjednostki zakresu, zwraca pełną odpowiedź. Znam Atom, ale nie jest to ogólne rozwiązanie stronicowania Rest. To nie jest rozwiązanie dla pojedynczego przypadku, a raczej to, jakie powinno być rozwiązanie ogólne. Nie wszystkie dokumenty / zbiory pasują do modelu Atom i nie ma powodu, aby go wymuszać, chyba że jest to wymagane.
Karl Guertin

1
@KarlGuertin Zgoda. Szkoda, że ​​jest to akceptowana odpowiedź, ponieważ wydaje się, że wielu członków społeczności faktycznie akceptuje Rangei Content-Rangesłuży do celów stronicowania.
Stijn de Witt

34

Nie zgadzam się z niektórymi z was. Od tygodni pracuję nad tą funkcją dla mojej usługi REST. To, co ostatecznie zrobiłem, jest naprawdę proste. Moje rozwiązanie ma sens tylko dla tego, co ludzie REST nazywają kolekcją.

Klient MUSI dołączyć nagłówek „Zakres”, aby wskazać, której części kolekcji potrzebuje, lub w inny sposób być gotowym do obsługi zbyt dużego błędu 413 ŻĄDANA JEDNOSTKA, gdy żądana kolekcja jest zbyt duża, aby można ją było pobrać w pojedynczym ruchu w obie strony.

Serwer wysyła odpowiedź 206 PARTIAL CONTENT, z nagłówkiem Content-Range określającym, która część zasobu została wysłana oraz z nagłówkiem ETag w celu zidentyfikowania bieżącej wersji kolekcji. Zwykle używam ETag podobnego do Facebooka {last_modification_timestamp} - {resource_id} i uważam, że ETag kolekcji to ten z ostatnio zmodyfikowanego zasobu, który zawiera.

Aby zażądać określonej części kolekcji, klient MUSI użyć nagłówka „Range” i wypełnić nagłówek „If-Match” tagiem ET kolekcji uzyskanym z wcześniej wykonanych żądań nabycia innych części tej samej kolekcji. Serwer może zatem sprawdzić, czy kolekcja nie uległa zmianie przed wysłaniem żądanej części. Jeśli istnieje nowsza wersja, zwracana jest odpowiedź 412 PRECONDITION FAILED, aby zaprosić klienta do pobrania kolekcji od podstaw. Jest to konieczne, ponieważ może to oznaczać, że niektóre zasoby mogły zostać dodane lub usunięte przed lub po aktualnie żądanej części.

Używam ETag / If-Match w połączeniu z Last-Modified / If-Unmodified-Since, aby zoptymalizować pamięć podręczną. Przeglądarki i serwery proxy mogą polegać na jednym lub obu z nich w zakresie algorytmów buforowania.

Uważam, że adres URL powinien być czysty, chyba że zawiera zapytanie wyszukiwania / filtru. Jeśli się nad tym zastanowić, wyszukiwanie to nic innego jak częściowy widok zbioru. Zamiast adresów URL samochody / wyszukiwanie? Q = BMW, powinniśmy zobaczyć więcej samochodów? Producent = BMW.


Czy chodziło Ci o 416 „Requested Range Not Satisfiable” lub „413” Request Entity Too Large?

1
@Mohamed Myślę, że masz na myśli If-Unmodified-Since, co odpowiada wariantowi E-Tag If-Match, a nie If-Modified-Since. To powiedziawszy, możesz również rozważyć usunięcie tego ograniczenia, w zależności od przypadku użycia. Załóżmy, że masz kolekcję, która rośnie tylko z góry (jak niektóre kolekcje w stylu „najpierw najnowsze”). Najgorsze, co może się zdarzyć, jeśli kolekcja ta zmieni się między żądaniami, to fakt, że użytkownik przeglądający kolekcję widzi wpisy dwukrotnie. (Co samo w sobie jest również przydatną informacją: informuje użytkownika, że ​​kolekcja się zmieniła)
Eugene Beresovsky,

20
413 to „Żądany zbyt duży podmiot”, a nie „Żądany zbyt duży podmiot”. Oznacza to, że rozmiar Twojego żądania, na przykład podczas przesyłania pliku, jest większy niż serwer jest skłonny przetworzyć. Więc używanie go do tego nie wydaje się całkowicie właściwe.
user247702

@Mohamed Wiem, że to stare pytanie, ale jeśli ETag kolekcji jest ETag ostatnio zmodyfikowanego zasobu, który zawiera kolekcja, jakiej wartości nagłówka If-Match należy użyć podczas modyfikowania jednego zasobu w kolekcji? Używanie wartości ETag zwróconej z kolekcją jest błędne, ponieważ klient mógłby modyfikować zasób, nawet jeśli nie widzi ostatniego stanu zasobu.
Mickael Marrache

8
Zdecydowanie nie zgadzam się na używanie 413. Jest to kod błędu, który oznacza, że ​​klient wysyła coś, czego serwer odmawia przyjęcia ze względu na rozmiar. Nie na odwrót! Zobacz tools.ietf.org/html/rfc7231#section-6.5.11 (zwróć uwagę, że jest napisane żądanie ładunku. Brak odpowiedzi )!
exhuma

7

Nadal można wrócić Accept-Rangesi Content-Rangesz 200kodem odpowiedzi. Te dwa nagłówki odpowiedzi zapewniają wystarczającą ilość informacji, aby wywnioskować te same informacje, które 206jawnie dostarcza kod odpowiedzi.

RangeUżyłbym do paginacji i po prostu zwróciłbym a 200dla zwykłego GET.

Jest to w 100% RESTful i nie utrudnia przeglądania.

Edycja: napisałem post na blogu na ten temat: http://otac0n.com/blog/2012/11/21/range-header-i-choose-you.html


5

Jeśli jest więcej niż jedna strona odpowiedzi i nie chcesz oferować całej kolekcji naraz, czy to oznacza, że ​​istnieje wiele możliwości wyboru?

Na żądanie do /db/questions, wróć 300 Multiple Choicesz Linknagłówkami, które określają, jak dostać się do każdej strony, a także do obiektu JSON lub strony HTML z listą adresów URL.

Link: <>; rel="http://paged.collection.example/relation/paged"
Link: <>; rel="http://paged.collection.example/relation/paged"
...

Będziesz mieć jeden Linknagłówek dla każdej strony wyników (pusty ciąg oznacza aktualny adres URL, a adres URL jest taki sam dla każdej strony, tylko dostępny z różnymi zakresami), a relacja jest definiowana jako niestandardowa zgodnie z nadchodzącą Linkspecyfikacją . Ten związek wyjaśniałby twój zwyczaj 266lub naruszenie 206. Te nagłówki są twoją wersją do odczytu maszynowego, ponieważ wszystkie twoje przykłady i tak wymagają zrozumienia klienta.

(Jeśli trzymasz się trasy „zakres”, uważam 2xx, że najlepszym zachowaniem byłby twój własny kod powrotu, tak jak go opisałeś. Oczekuje się, że zrobisz to dla swoich aplikacji i takich [„Kody stanu HTTP są rozszerzalne. „] i masz dobre powody).

300 Multiple Choicesmówi, że POWINIENEŚ również udostępnić treść, w której agent użytkownika może wybierać. Jeśli klient rozumie, powinien użyć Linknagłówków. Jeśli jest to użytkownik przeglądający ręcznie, być może strona HTML z linkami do specjalnego, „stronicowanego” zasobu głównego, który może obsłużyć renderowanie tej strony na podstawie adresu URL? /humanpage/1/db/questionsczy coś takiego ohydnego?


Komentarze do postu Richarda Levasseura przypominają mi o dodatkowej opcji: Acceptnagłówku (sekcja 14.1). Kiedy pojawiła się specyfikacja oEmbed, zastanawiałem się, dlaczego nie została wykonana w całości przy użyciu protokołu HTTP, i napisałem alternatywę, używając ich.

Zachowaj 300 Multiple Choices, Linknagłówki i stronę HTML dla początkowego naiwnego HTTP GET, ale zamiast używać zakresów, użyj nowej relacji stronicowania, aby zdefiniować użycie Acceptnagłówka. Twoje kolejne żądanie HTTP może wyglądać następująco:

GET /db/questions HTTP/1.1
Host: paged.collection.example
Accept: application/json;PagingSpec=1.0;page=1

AcceptNagłówka pozwala określić akceptowalny typ zawartości (zwrot JSON), a także parametry rozszerzalne dla danego typu (numer strony). Riffując moje notatki z mojego wpisu oEmbed (nie mogę tutaj utworzyć linku, wymienię to w moim profilu), możesz być bardzo wyraźny i podać tutaj wersję specyfikacji / relacji na wypadek, gdybyś musiał przedefiniować znaczenie pageparametru w przyszłości.


1
+1 do nagłówków linków, ale poleciłbym również wspólne pierwsze, poprzednie, następne, ostatnie rels, a także poprzednie archiwum RFC5005, następne archiwum i bieżące.
Joseph Holsten,

> Na żądanie do / db / questions zwróć 300 Multiple Choices z nagłówkami Link, które określają, jak dostać się do każdej strony [..] Problem z tym (iz większością czystych projektów REST) ​​polega na tym, że zabija to ze względu na opóźnienia. Celem jest zminimalizowanie żądań sieciowych. To pierwsze żądanie powinno przynieść rezultaty, a nie linki do kolejnych żądań, które ostatecznie dostarczą potrzebnych nam danych.
Stijn de Witt

4

Edytować:

Po dłuższym przemyśleniu jestem skłonny zgodzić się, że nagłówki zakresów nie są odpowiednie do paginacji. Logika jest taka, że ​​nagłówek Range jest przeznaczony do odpowiedzi serwera, a nie aplikacji. Jeśli podałeś 100 megabajtów wyników, ale serwer (lub klient) mógł przetwarzać tylko 1 megabajt naraz, cóż, do tego służy nagłówek Range.

Jestem też zdania, że ​​podzbiór zasobów jest własnym zasobem (podobnie jak algebra relacyjna), więc zasługuje na przedstawienie w adresie URL.

Zasadniczo wycofuję swoją pierwotną odpowiedź (poniżej) dotyczącą używania nagłówka.


Myślę, że odpowiedziałeś mniej więcej na swoje pytanie - zwróć 200 lub 206 z zakresem zawartości i opcjonalnie użyj parametru zapytania. Sniperowałbym agenta użytkownika i typ zawartości i, w zależności od nich, sprawdzał parametr zapytania. W przeciwnym razie wymagaj nagłówków zakresów.

Zasadniczo masz sprzeczne cele - pozwól ludziom używać przeglądarki do przeglądania (co nie pozwala łatwo na niestandardowe nagłówki) lub zmusić ludzi do korzystania ze specjalnego klienta, który może ustawiać nagłówki (co nie pozwala im odkrywać).

Możesz po prostu dostarczyć im specjalnego klienta w zależności od żądania - jeśli wygląda jak zwykła przeglądarka, wyślij małą aplikację Ajax, która renderuje stronę i ustawia niezbędne nagłówki.

Oczywiście toczy się również debata na temat tego, czy adres URL powinien zawierać wszystkie niezbędne informacje o tego rodzaju rzeczach. Określanie zakresu za pomocą nagłówków może być przez niektórych uważane za „niespokojne”.

Nawiasem mówiąc, byłoby miło, gdyby serwery mogły odpowiedzieć nagłówkiem „Can-Specify: Header1, header2”, a przeglądarki internetowe przedstawiałyby interfejs użytkownika, aby użytkownicy mogli wpisać wartości, jeśli chcą.


Dzięki za odpowiedzi. Myślałem o tym temacie, ale liczyłem na drugą opinię. Czy zdarzyło się, że masz wskaźnik do argumentów nagłówka?
Karl Guertin

Oto jedyna, którą dodałem do zakładek (zobacz dyskusję w komentarzach): barelyenough.org/blog/2008/05/versioning-rest-web-services Inna witryna dotyczyła wykorzystania przez Rubiego .json, .xml, cokolwiek w określaniu typ treści żądania. Oto kilka przykładów: * język - umieszczenie go w adresie URL oznacza wysłanie linku do innego kraju spowodowałoby, że byłby on w złym języku. * paginacja - Umieszczenie go w nagłówku oznacza, że ​​nie możesz łączyć ludzi z tym, co widzisz
Richard Levasseur

* typ treści: połączenie problemów z językiem i paginacją - jeśli jest w adresie URL, co jeśli klient nie obsługuje tego typu treści (np. rozszerzenie .ajax i .html)? I odwrotnie, bez tego typu treści w adresie URL nie można zapewnić takiej samej reprezentacji. „nowa witryna Ajax! example.com/cool.ajax” vs „fajny artykuł tutaj: example.com/article.ajax#id=123”.
Richard Levasseur

2
IMO, czy trafi w adres URL, czy nie, zależy od tego, co to jest. Moja ogólna zasada jest taka, że ​​jeśli miałby zidentyfikować konkretny zasób (czy to zasób w określonym stanie, wybór zasobów czy dyskretny wynik), trafia do adresu URL. Wyszukiwane hasła, paginacja i spokojne transakcje są tego dobrymi przykładami. Jeśli jest to coś, co jest potrzebne do przekształcenia abstrakcyjnej reprezentacji w konkretną reprezentację, trafia do nagłówka. informacje auth i typ treści są tego dobrymi przykładami.
Richard Levasseur

Myślę o ciągu zapytania w adresie URL jako o opcjach odpytywania określonego zasobu.
wprl

3

Możesz rozważyć użycie modelu podobnego do protokołu Atom Feed Protocol, ponieważ ma on rozsądny model zbiorów HTTP i sposób ich manipulowania (gdzie szaleństwo oznacza WebDAV).

Istnieje protokół publikowania Atom, który definiuje model kolekcji i operacje REST, a ponadto możesz użyć RFC 5005 - stronicowanie i archiwizowanie kanałów, aby przeglądać duże kolekcje.

Przełączenie z Atom XML na zawartość JSON nie powinno mieć wpływu na pomysł.


3

Myślę, że prawdziwym problemem jest to, że w specyfikacji nie ma nic, co mówi nam, jak wykonać automatyczne przekierowania w obliczu 413 - Requested Entity Too Large.

Ostatnio borykałem się z tym samym problemem i szukałem inspiracji w książce RESTful Web Services . Osobiście uważam, że 206 nie jest odpowiednie ze względu na wymagania dotyczące nagłówka. Moje myśli również doprowadziły mnie do 300, ale pomyślałem, że jest to bardziej dla różnych typów mime, więc poszukałem tego, co Richardson i Ruby mieli do powiedzenia na ten temat w Dodatku B, strona 377. Sugerują, że serwer wybiera po prostu preferowaną reprezentacji i odeślij ją z 200, zasadniczo ignorując pogląd, że powinna to być 300.

To również kłóci się z pojęciem powiązań z następnymi zasobami, które mamy z atomu. Rozwiązanie, które zaimplementowałem, polegało na dodaniu kluczy „next” i „previous” do mapy json, którą odesłałem i skończyło się z tym.

Później zacząłem myśleć, że być może należy wysłać 307 - Tymczasowe przekierowanie do odsyłacza, który wyglądałby na coś takiego jak / db / questions / 1,25 - co pozostawia oryginalny URI jako kanoniczną nazwę zasobu, ale prowadzi do odpowiednio nazwany podrzędny zasób. To jest zachowanie, które chciałbym zobaczyć w przypadku 413, ale 307 wydaje się dobrym kompromisem. Jednak nie próbowałem jeszcze tego w kodzie. Jeszcze lepsze byłoby przekierowanie do adresu URL zawierającego rzeczywiste identyfikatory ostatnio zadawanych pytań. Na przykład, jeśli każde pytanie ma identyfikator całkowity, aw systemie jest 100 pytań i chcesz wyświetlić dziesięć ostatnich, żądania do / db / pytania powinny mieć 307 do / db / questions / 100,91

To bardzo dobre pytanie, dziękuję, że je zadałeś. Potwierdziłeś mi, że nie jestem wariatem, że spędziłem dni na rozmyślaniu o tym.


303 byłby lepszy pod tym względem niż 307. 307 oznacza, że ​​oryginalny adres URL wkrótce zacznie odpowiadać zgodnie z oczekiwaniami klienta.
Nicholas Shanks

RFC 7231 odwołuje się do kodu stanu HTTP 413 jako zbyt duży ładunek i wiąże ten kod z rozmiarem żądania, a nie potencjalnym rozmiarem odpowiedzi.
beawolf

1

Możesz wykryć Rangenagłówek i naśladować Dojo, jeśli jest obecny, i naśladować Atom, jeśli nie jest. Wydaje mi się, że to zgrabnie dzieli przypadki użycia. Jeśli odpowiadasz na zapytanie REST z aplikacji, spodziewasz się, że zostanie ono sformatowane za pomocą Rangenagłówka. Jeśli odpowiadasz zwykłej przeglądarce, to jeśli zwrócisz linki stronicowania, narzędzie zapewni łatwy sposób przeglądania kolekcji.


1

Jednym z największych problemów z nagłówkami zakresów jest to, że wiele korporacyjnych serwerów proxy je odfiltrowuje. Radziłbym zamiast tego użyć parametru zapytania.



0

Wydaje mi się, że najlepszym sposobem na to jest uwzględnienie zakresu jako parametrów zapytania. np. GET / db / questions /? date> mindate & date <maxdate . Po GET do / db / questions / bez parametrów zapytania, zwróć 303 z Location: / db / questions /? Query-parameters-to-retrieve-the-default-page . Następnie podaj inny adres URL, przez który ktoś korzysta z Twojego interfejsu API, aby uzyskać statystyki dotyczące kolekcji (np. Jakich parametrów zapytania użyć, jeśli chce mieć całą kolekcję);


0

Chociaż możliwe jest użycie w tym celu nagłówka Range, nie sądzę, aby taki był zamiar. Wygląda na to, że został zaprojektowany do obsługi niestabilnych połączeń, a także do ograniczania danych (więc klient może zażądać części żądania, jeśli czegoś brakowało lub rozmiar był zbyt duży, aby go przetworzyć). Włamujesz paginację do czegoś, co prawdopodobnie jest używane do innych celów w warstwie komunikacyjnej. „Właściwym” sposobem obsługi paginacji są zwracane typy. Zamiast zwracać obiekt pytania, powinieneś zamiast tego zwrócić nowy typ.

Więc jeśli pytania są takie:

<questions> <question index=1></question> <question index=2></question> ... </questions>

Nowy typ może wyglądać mniej więcej tak:

<questionPage> <startIndex>50</startIndex> <returnedCount>10</returnedCount> <totalCount>1203</totalCount> <questions> <question index=50></question> <question index=51></question> .. </questions> <questionPage>

Oczywiście masz kontrolę nad typami multimediów, dzięki czemu możesz tworzyć „strony” w formacie odpowiadającym Twoim potrzebom. Jeśli make jest czymś ogólnym, możesz mieć pojedynczy parser na kliencie do obsługi stronicowania tak samo dla wszystkich typów. Myślę, że jest to bardziej zgodne z duchem specyfikacji HTTP, a nie fałszowaniem parametru Range dla czegoś innego.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.