Пейджинг в коллекции Rest

Я заинтересован в предоставлении прямого интерфейса REST для коллекций документов JSON (например, CouchDB или Настойчиво). Проблема, с которой я сталкиваюсь, заключается в том, как обрабатывать операцию GET в корне коллекции, если коллекция большая.

В качестве примера представьте, что я выставляю таблицу StackOverflow Questions, где каждая строка представлена ​​​​как документ (не обязательно такая таблица, просто конкретный пример значительной коллекции «документов»). Коллекция будет доступна на /db/questions с использованием обычного API CRUD GET /db/questions/XXX, PUT /db/questions/XXX, POST /db/questions. Стандартный способ получить всю коллекцию — это GET /db/questions, но если он наивно выгружает каждую строку как объект JSON, вы получите довольно большую загрузку и много работы со стороны сервера.

Решение, конечно же, пейджинг. Dojo решил эту проблему в своем JsonRestStore с помощью разумного расширения, совместимого с RFC2616, для использования заголовок Range с пользовательской единицей диапазона items. Результатом является 206 Partial Content, который возвращает только запрошенный диапазон. Преимущество этого подхода по сравнению с параметром запроса заключается в том, что он оставляет строку запроса для... запросов (например, GET /db/questions/?score>200 или что-то в этом роде, и да, это будет закодировано %3E).

Этот подход полностью охватывает поведение, которое я хочу. Проблема в том, что RFC 2616 указывает, что в ответе 206 (выделено мной):

Запрос ДОЛЖЕН содержать поле заголовка Range (раздел 14.35) с указанием желаемого диапазона и МОЖЕТ включать поле заголовка If-Range (раздел 14.27).), чтобы сделать запрос условным.

Это имеет смысл в контексте стандартного использования заголовка, но является проблемой, потому что я бы хотел, чтобы ответ 206 был по умолчанию для обработки наивных клиентов/случайных людей, исследующих.

Я подробно изучил RFC в поисках решения, но был недоволен своими решениями и заинтересован в том, чтобы SO решил проблему.

Идеи, которые у меня были:

  • Верните 200 с заголовком Content-Range! – я не думаю, что это неправильно, но я бы предпочел более очевидный индикатор того, что ответ является только частичным содержанием.
  • Возврат 400 Range Required. Для обязательных заголовков не существует специального кода ответа 400, поэтому необходимо использовать ошибку по умолчанию и читать ее вручную. Это также затрудняет исследование через веб-браузер (или какой-либо другой клиент, такой как Resty).
  • Использовать параметр запроса. Стандартный подход, но я надеюсь разрешить запросы а-ля Persevere, и это сокращает пространство имен запроса.
  • Просто верните 206! — я думаю, что большинство клиентов не будут волноваться, но я бы не стал нарушать ДОЛЖЕН в RFC
  • Расширьте спецификацию! Возврат 266 Partial Content — ведет себя точно так же, как 206, но является ответом на запрос, который НЕ ДОЛЖЕН содержать заголовок Range. Я полагаю, что 266 достаточно высок, чтобы не столкнуться с проблемами коллизий, и для меня это имеет смысл, но я не понимаю, считается ли это табу или нет.

Я думаю, что это довольно распространенная проблема, и я хотел бы, чтобы это делалось де-факто, чтобы я или кто-то еще не изобретал велосипед.

Каков наилучший способ предоставить полную коллекцию через HTTP, когда коллекция большая?


person Karl Guertin    schedule 29.05.2009    source источник
comment
Вау, это хороший пример вопроса, над которым уже серьезно поразмышляли.   -  person Heiko Rupp    schedule 18.02.2013
comment
возможный дубликат разбивки на страницы в веб-приложении REST   -  person rds    schedule 25.05.2013
comment
Что касается подхода Dojo к использованию заголовка Range, хотя Accept-Ranges допускает расширение, насколько я могу судить, EBNF для Range этого не делает: tools.ietf.org/html/rfc2616#section-14.35.2 . Спецификация указывает Range = "Range" ":" ranges-specifier, где последний в tools.ietf.org/html/rfc2616# section-14.35.1 описывается просто как спецификатор диапазонов байтов, который должен начинаться с блока байтов, который определяется как строка байтов.   -  person Brett Zamir    schedule 21.06.2013
comment
Заголовок Content-Range применяется к телу (может использоваться с запросом при загрузке больших файлов и т. д. или для ответа при загрузке). Заголовок Range используется для запроса определенного диапазона. Следует ответить 206, когда заголовок Range был включен в запрос. Если это не так, ответ может по-прежнему включать заголовок Content-Range, но код ответа должен быть 200. Этот заголовок на самом деле кажется идеальным для пейджинга.   -  person Stijn de Witt    schedule 10.05.2017
comment
Но сам RFC 2616 говорит, что реализации HTTP/1.1 МОГУТ игнорировать диапазоны, указанные с использованием других единиц измерения. Так хорошо ли использовать заголовки Range для разбиения на страницы? потому что это может поставить под угрозу совместимость.   -  person chetan choulwar    schedule 05.09.2018


Ответы (12)


Я чувствую, что расширения диапазона HTTP не предназначены для вашего варианта использования, и поэтому вам не следует пытаться. Частичный ответ подразумевает 206, а 206 должен быть отправлен только в том случае, если клиент запросил его.

Возможно, вы захотите рассмотреть другой подход, например тот, который используется в Atom (где представление по дизайну может быть частичным и возвращается со статусом 200 и, возможно, ссылками на страницы). См. RFC 4287 и RFC 5005.

person Julian Reschke    schedule 29.05.2009
comment
Использование Dojo полностью соответствует спецификации. Если сервер не понимает единицу диапазона items, он возвращает полный ответ. Я знаком с Atom, но это не общее решение для пейджинга Rest. Это не решение для одного случая, а скорее общее решение. Не все документы/коллекции соответствуют модели Atom, и нет причин навязывать ее без необходимости. - person Karl Guertin; 01.06.2009
comment
@КарлГуэртин Согласен. Жаль, что это принятый ответ, потому что кажется, что многие в сообществе на самом деле используют Range и Content-Range для целей пейджинга. - person Stijn de Witt; 10.05.2017

Я не совсем согласен с некоторыми из вас, ребята. Я несколько недель работал над этой функцией для своего сервиса REST. То, что я в итоге сделал, очень просто. Мое решение имеет смысл только для того, что люди REST называют коллекцией.

Клиент ДОЛЖЕН включать заголовок «Диапазон», чтобы указать, какая часть коллекции ему нужна, или иным образом быть готовым обработать ошибку 413 REQUESTED ENTITY TOO LARGE, когда запрошенная коллекция слишком велика для получения за один цикл.

Сервер отправляет ответ 206 PARTIAL CONTENT с заголовком Content-Range, указывающим, какая часть ресурса была отправлена, и заголовком ETag, определяющим текущую версию коллекции. Я обычно использую Facebook-подобный ETag {last_modification_timestamp}-{resource_id} и считаю, что ETag коллекции — это ETag последнего измененного ресурса, который она содержит.

Чтобы запросить определенную часть коллекции, клиент ДОЛЖЕН использовать заголовок «Range» и заполнить заголовок «If-Match» ETag коллекции, полученной из ранее выполненных запросов на получение других частей той же коллекции. Таким образом, перед отправкой запрошенной части сервер может убедиться, что коллекция не изменилась. Если существует более поздняя версия, возвращается ответ 412 PRECONDITION FAILED, чтобы предложить клиенту получить коллекцию с нуля. Это необходимо, потому что это может означать, что некоторые ресурсы могли быть добавлены или удалены до или после текущей запрошенной части.

Я использую ETag/If-Match в тандеме с Last-Modified/If-Unmodified-Since для оптимизации кеша. Браузеры и прокси-серверы могут полагаться на один или оба из них в своих алгоритмах кэширования.

Я думаю, что URL-адрес должен быть чистым, если он не включает поисковый/фильтрующий запрос. Если подумать, поиск — это не что иное, как частичный просмотр коллекции. Вместо URL-адресов типа cars/search?q=BMW мы должны увидеть больше cars?manufacturer=BMW.

person Mohamed    schedule 23.06.2009
comment
Вы имели в виду 416 Requested Range Not Satisfiable или 413 Request Entity Too Large? - person ; 18.09.2012
comment
@ Мохамед Мохамед, я думаю, вы имеете в виду If-Unmodified-Since, что соответствует варианту E-Tag If-Match, а не If-Modified-Since. Тем не менее, вы также можете рассмотреть возможность удаления этого ограничения, в зависимости от вашего варианта использования. Скажем, у вас есть коллекция, которая растет только сверху (например, какая-то новейшая коллекция первого стиля), самое худшее, что может случиться, если эта коллекция изменится между запросами, — это то, что пользователь, просматривающий коллекцию, увидит записи дважды. (Что само по себе также является полезной информацией: она сообщает пользователю, что коллекция изменилась) - person Evgeniy Berezovsky; 07.01.2013
comment
413 — слишком большой объект запроса, а не слишком большой запрошенный объект. Это означает, что размер вашего запроса, например, при загрузке файла, больше, чем сервер готов обработать. Так что использовать его для этого, кажется, не совсем уместно. - person user247702; 29.05.2013
comment
@Mohamed Я знаю, что это старый вопрос, но если ETag коллекции является ETag последнего измененного ресурса, содержащегося в коллекции, какое значение заголовка If-Match следует использовать при изменении одного ресурса в коллекции? Использование значения ETag, возвращаемого вместе с коллекцией, является неправильным, поскольку клиент сможет изменить ресурс, даже если он не увидит последнее состояние ресурса. - person Mickael Marrache; 15.02.2015
comment
@MickaelMarrache Каждый ресурс, содержащийся в коллекции, проецируется с собственным ETag в ответе. - person Mohamed; 23.02.2015
comment
Вы говорите, что поиск — это не более чем частичный просмотр коллекции. То же самое относится и к странице. Извлечение страницы также является фильтрацией. Нет ничего плохого в параметрах страницы в URL-адресах, если связь между ресурсами страницы и ресурсом коллекции ясна. См., например. w3.org/community/hydra/wiki/Collection_Design#Pagination - person letmaik; 14.11.2015
comment
Я категорически не согласен с использованием 413. Это код ошибки, означающий, что клиент отправляет что-то, что сервер отказывается принимать из-за размера. А не наоборот! См. tools.ietf.org/html/rfc7231#section-6.5.11 (обратите внимание, что здесь написано запрос полезной нагрузки, а не ответ полезной нагрузки)! - person exhuma; 22.12.2015
comment
@neo Интересный аспект. Это та же идея, на которую указывает Джулиан Решке с tools.ietf.org/html/rfc5005#section. -3 В сочетании с HAL datatracker.ietf.org/doc /draft-kelly-json-hal интересное решение - person Raphael Bossek; 12.07.2016
comment
@exhuma какую альтернативу вы бы порекомендовали? 416 Requested Range Not Satisfiable кажется хорошим, за исключением того, что клиент не запросил диапазон, поэтому это может быть неожиданно. 507 Insufficient Storage — еще один вариант, но для него, вероятно, потребуется удобочитаемое сообщение об ошибке, чтобы указать, почему вы отклоняете запрос. Я не вижу хорошего, полностью автоматического, соответствующего стандартам способа сообщить клиенту, что необходимо использовать заголовки диапазона. - person ngreen; 15.04.2019
comment
@ngreen 507 является частью стандарта WebDAV и означает, что на сервере недостаточно места для хранения ресурса, отправленного клиентом. Так что семантика очень разная. И 416 относится к запросам Range, которые вызывают проблемы, главным образом потому, что диапазоны определены только для байтов, а прокси-серверу разрешено отбрасывать эти заголовки. В настоящее время я не вижу стандартного способа, кроме текущего главного ответа. Это требует более широкого обсуждения, которое не вписывается в комментарии SO. Может быть, на chat.stackoverflow.com? - person exhuma; 16.04.2019

Вы по-прежнему можете вернуть Accept-Ranges и Content-Ranges с кодом ответа 200. Эти два заголовка ответа дают вам достаточно информации, чтобы выводить ту же информацию, которую явно предоставляет код ответа 206.

Я бы использовал Range для нумерации страниц, и просто возвращал бы 200 вместо обычного GET.

Это выглядит на 100% RESTful и не делает просмотр более сложным.

Изменить: я написал об этом сообщение в блоге: http://otac0n.com/blog/2012/11/21/range-header-i-choose-you.html

person John Gietzen    schedule 14.11.2012

Если есть более одной страницы ответов, и вы не хотите предлагать всю коллекцию сразу, означает ли это, что есть несколько вариантов?

На запрос к /db/questions возвращайте 300 Multiple Choices с заголовками Link, которые указывают, как добраться до каждой страницы, а также объект JSON или HTML-страницу со списком URL-адресов.

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

У вас будет один заголовок Link для каждой страницы результатов (пустая строка означает текущий URL-адрес, а URL-адрес один и тот же для каждой страницы, доступ к которой осуществляется с разными диапазонами), а отношение определяется как специальный в соответствии с готовящейся Link спецификацией. Эта связь могла бы объяснить ваш обычай 266 или нарушение вами 206. Эти заголовки являются вашей машиночитаемой версией, поскольку все ваши примеры в любом случае требуют понимающего клиента.

(Если вы придерживаетесь маршрута «диапазон», я считаю, что ваш собственный код возврата 2xx, как вы его описали, будет лучшим поведением здесь. Ожидается, что вы сделаете это для своих приложений, и такие ["коды состояния HTTP расширяемы ."], и у вас есть веские причины.)

300 Multiple Choices говорит, что вы ДОЛЖНЫ также предоставить тело с возможностью выбора пользовательским агентом. Если ваш клиент понимает, он должен использовать заголовки Link. Если это пользователь, просматривающий вручную, может быть, HTML-страница со ссылками на специальный корневой ресурс с «выгружаемыми страницами», который может обрабатывать рендеринг этой конкретной страницы на основе URL-адреса? /humanpage/1/db/questions или что-то в этом роде?


Комментарии к сообщению Ричарда Левассера напоминают мне о дополнительной опции: заголовке Accept (раздел 14.1). Когда вышла спецификация oEmbed, я задался вопросом, почему это не было сделано полностью с использованием HTTP, и написал альтернативу с их использованием.

Сохраните заголовки 300 Multiple Choices, Link и HTML-страницу для исходного наивного HTTP GET, но вместо использования диапазонов задайте новые отношения пейджинга с использованием заголовка Accept. Ваш последующий HTTP-запрос может выглядеть так:

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

Заголовок Accept позволяет вам определить приемлемый тип контента (возврат JSON), а также расширяемые параметры для этого типа (номер вашей страницы). Ссылаясь на мои заметки из моего описания oEmbed (не могу дать ссылку на него здесь, я укажу его в своем профиле), вы могли бы быть очень явным и предоставить здесь версию спецификации/отношения на случай, если вам нужно переопределить, что такое параметр page значит в будущем.

person Vitorio    schedule 04.06.2009
comment
+1 заголовки ссылок, но я бы также рекомендовал общие first, prev, next, last rels, а также prev-archive, next-archive и current RFC5005. - person Joseph Holsten; 05.09.2009
comment
По запросу к /db/questions возвращайте 300 множественных вариантов выбора с заголовками ссылок, указывающими, как добраться до каждой страницы [..] Проблема с этим (и с большинством чистых REST-проектов) заключается в том, что это убивает задержку. Цель состоит в том, чтобы свести к минимуму сетевые запросы. Этот первый запрос должен давать результаты, а не ссылки на другие запросы, которые в конечном итоге дадут нужные нам данные. - person Stijn de Witt; 10.05.2017
comment
вы также можете проверить образец, созданный Google для Android github.com/android/architecture-components-samples/blob/main/ Проверьте класс ApiSuccessResponse - person Rahul; 30.04.2021

Редактировать:

Немного подумав об этом, я склонен согласиться с тем, что заголовки Range не подходят для нумерации страниц. Логика заключается в том, что заголовок Range предназначен для ответа сервера, а не приложений. Если вы обработали 100 мегабайт результатов, но сервер (или клиент) мог обрабатывать только 1 мегабайт за раз, для этого и предназначен заголовок Range.

Я также придерживаюсь мнения, что подмножество ресурсов является собственным ресурсом (аналогично реляционной алгебре), поэтому оно заслуживает представления в URL-адресе.

Итак, в основном, я отказываюсь от своего первоначального ответа (ниже) об использовании заголовка.


Я думаю, вы более или менее ответили на свой вопрос - верните 200 или 206 с диапазоном содержимого и, возможно, используйте параметр запроса. Я бы понюхал пользовательский агент и тип контента и, в зависимости от них, проверил параметр запроса. В противном случае потребуются заголовки диапазона.

По сути, у вас противоречивые цели — позволить людям использовать свой браузер для изучения (который не позволяет легко создавать собственные заголовки) или заставить людей использовать специальный клиент, который может устанавливать заголовки (который не позволяет им исследовать).

Вы можете просто предоставить им специальный клиент в зависимости от запроса - если он выглядит как обычный браузер, отправьте небольшое приложение ajax, которое отображает страницу и устанавливает необходимые заголовки.

Конечно, также ведутся споры о том, должен ли URL-адрес содержать все необходимые состояния для такого рода вещей. Указание диапазона с помощью заголовков может быть сочтено некоторыми "беспокойным".

Кроме того, было бы неплохо, если бы серверы могли отвечать заголовком «Can-Specify: Header1, header2», а веб-браузеры представляли бы пользовательский интерфейс, чтобы пользователи могли вводить значения, если они того пожелают.

person Richard Levasseur    schedule 03.06.2009
comment
Спасибо за ответ. Я думал об этой теме, но надеялся получить второе мнение. У вас есть указатель на аргументы заголовка? - person Karl Guertin; 03.06.2009
comment
Вот единственный, который я добавил в закладки (см. обсуждение в комментариях): barelyenough.org/blog/2008/05/versioning-rest-web-services Другой сайт вращался вокруг использования Ruby .json, .xml, .whatever при определении типа содержимого запроса. Вот некоторые из примеров: * язык — указание его в URL-адресе означает, что отправка ссылки в другую страну будет отображать ее на неправильном языке. * разбивка на страницы — размещение в заголовке означает, что вы не можете связать людей с тем, что видите - person Richard Levasseur; 04.06.2009
comment
* тип содержимого: сочетание языковых проблем и проблем с нумерацией страниц. Если это указано в URL-адресе, что, если клиент не поддерживает этот тип содержимого (например, расширение .ajax и .html)? И наоборот, без этого типа содержимого в URL-адресе вы не можете гарантировать, что будет дано такое же представление. новый сайт ajax! example.com/cool.ajax и классная статья здесь: example.com/article.ajax#id=123. - person Richard Levasseur; 04.06.2009
comment
ИМО, идет ли он в URL-адресе или нет, зависит от того, что это такое. Мое общее правило заключается в том, что если он идентифицирует конкретный ресурс (будь то ресурс в определенном состоянии, выбор ресурсов или дискретный результат), он указывается в URL-адресе. Хорошими примерами этого являются поисковые запросы, разбиение на страницы и спокойные транзакции. Если это необходимо для преобразования абстрактного представления в конкретное представление, это указывается в заголовке. auth info и content-type являются хорошими примерами этого. - person Richard Levasseur; 04.06.2009
comment
Я думаю о строке запроса в URL-адресе как о параметрах для запроса указанного ресурса. - person wprl; 25.06.2013

Вы можете рассмотреть возможность использования модели, похожей на протокол Atom Feed, поскольку у него есть разумная HTTP-модель коллекций и способы манипулирования ими (где безумие означает WebDAV).

Существует протокол публикации Atom, который определяет модель сбора и операции REST, плюс вы можете использовать RFC 5005 — пейджинг и архивирование каналов для просмотра больших коллекций.

Переход с содержимого Atom XML на содержимое JSON не должен повлиять на эту идею.

person dajobe    schedule 03.06.2009

Я думаю, что настоящая проблема здесь в том, что в спецификации нет ничего, что говорило бы нам, как делать автоматические перенаправления при столкновении с 413 — запрошенный объект слишком велик.

Недавно я боролся с той же проблемой и искал вдохновение в книге RESTful Web Services. Лично я не думаю, что 206 подходит из-за требований к заголовку. Мои мысли также привели меня к 300, но я подумал, что это больше для разных типов пантомимы, поэтому я посмотрел, что Ричардсон и Руби сказали по этому вопросу в Приложении B, стр. 377. Они предлагают, чтобы сервер просто выбрал предпочитаемый тип. представление и отправить его обратно с 200, фактически игнорируя представление о том, что это должно быть 300.

Это также согласуется с понятием ссылок на следующие ресурсы, которые у нас есть от атома. Решение, которое я реализовал, заключалось в том, чтобы добавить ключи «следующий» и «предыдущий» к карте json, которую я отправлял обратно, и покончить с этим.

Позже я начал думать, что, может быть, нужно отправить 307 — временное перенаправление на ссылку, которая будет выглядеть примерно так: /db/questions/1,25 — что оставляет исходный URI в качестве канонического имени ресурса, но дает вам подчиненный ресурс с соответствующим названием. Это поведение, которое я хотел бы видеть от 413, но 307 кажется хорошим компромиссом. На самом деле еще не пробовал это в коде. Что было бы еще лучше, так это перенаправление на URL-адрес, содержащий фактические идентификаторы последних заданных вопросов. Например, если каждый вопрос имеет целочисленный идентификатор, а в системе 100 вопросов, и вы хотите отобразить десять самых последних, запросы к /db/questions должны быть преобразованы с кодом 307 в /db/questions/100,91.

Это очень хороший вопрос, спасибо, что задали его. Вы подтвердили мне, что я не сошел с ума, раз думая об этом целыми днями.

person stinkymatt    schedule 04.06.2009
comment
В этом отношении 303 будет лучше, чем 307. 307 подразумевает, что исходный URL-адрес вскоре начнет отвечать так, как ожидает клиент. - person Nicholas Shanks; 08.11.2012
comment
RFC 7231 ссылается на код состояния HTTP 413 как Слишком большая полезная нагрузка и связывает этот код с размером запроса, а не с потенциальным размером ответа. - person Mustafa Çağatay Tulun; 02.10.2018

Одна из больших проблем с заголовками диапазонов заключается в том, что многие корпоративные прокси-серверы их отфильтровывают. Вместо этого я бы посоветовал использовать параметр запроса.

person user64141    schedule 15.08.2014

С публикацией rfc723x незарегистрированные единицы диапазона действительно противоречат явной рекомендации в спец.. Рассмотрим rfc7233 (устаревший rfc2616):

«Новые единицы диапазона должны быть зарегистрированы в IANA" (вместе со ссылкой на a Реестр модулей диапазона HTTP).

person Sam    schedule 27.06.2014

Вы можете обнаружить заголовок Range и имитировать Dojo, если он присутствует, и имитировать Atom, если его нет. Мне кажется, что это четко разделяет варианты использования. Если вы отвечаете на запрос REST из своего приложения, вы ожидаете, что он будет отформатирован с заголовком Range. Если вы отвечаете обычному браузеру, то если вы вернете ссылки на страницы, это позволит инструменту предоставить простой способ изучения коллекции.

person Greg    schedule 04.06.2009

Мне кажется, что лучший способ сделать это - включить диапазон в качестве параметров запроса. например, GET /db/questions/?date>mindate&date‹maxdate. После GET к /db/questions/ без параметров запроса верните 303 с Расположением: /db/questions/?query-parameters-to-retrive-the-default-page. Затем укажите другой URL-адрес, по которому тот, кто использует ваш API, будет получать статистику о коллекции (например, какие параметры запроса использовать, если ему нужна вся коллекция);

person Dathan    schedule 05.06.2009

Хотя для этой цели можно использовать заголовок Range, я не думаю, что это было намерением. Похоже, он был разработан для обработки ненадежных соединений, а также для ограничения данных (чтобы клиент мог запросить часть запроса, если что-то отсутствует или размер слишком велик для обработки). Вы превращаете разбивку на страницы во что-то, что, вероятно, используется для других целей на коммуникационном уровне. "Правильный" способ обработки нумерации страниц - с типами, которые вы возвращаете. Вместо того, чтобы возвращать объект вопросов, вы должны вместо этого возвращать новый тип.

Итак, если вопросы такие:

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

Новый тип может быть примерно таким:

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

Конечно, вы управляете своими типами мультимедиа, поэтому вы можете сделать свои «страницы» форматом, который соответствует вашим потребностям. Если вы делаете что-то общее, у вас может быть один анализатор на клиенте для обработки пейджинга одинаково для всех типов. Я думаю, что это больше в духе спецификации HTTP, а не подделка параметра Range для чего-то другого.

person jeremyh    schedule 03.10.2014