Я разрабатываю сервис REST API для большого сайта социальной сети, в котором я участвую. Пока что он работает отлично. Я могу выдать GET
, POST
, PUT
и DELETE
запросы на объект URL - адресов и влияет на мои данные. Однако эти данные разбиты на страницы (ограничено 30 результатами одновременно).
Тем не менее, что было бы лучшим RESTful-способом для получения общего числа участников, скажем, через мой API?
В настоящее время я выдаю запросы к структуре URL, например:
- / api / members - возвращает список участников (30 одновременно, как указано выше)
- / api / members / 1 - влияет на одного члена в зависимости от используемого метода запроса
Мой вопрос: как бы я тогда использовал похожую структуру URL, чтобы получить общее количество участников в моем приложении? Очевидно, что запрос только id
поля (аналогично API Graph в Facebook) и подсчет результатов будут неэффективными, поскольку будет возвращен только фрагмент из 30 результатов.
источник
Ответы:
Хотя ответ на / API / users разбит на страницы и возвращает только 30 записей, ничто не мешает вам включить в ответ также общее количество записей и другую соответствующую информацию, такую как размер страницы, номер страницы / смещение и т. Д. ,
API StackOverflow - хороший пример того же дизайна. Вот документация для метода Users - https://api.stackexchange.com/docs/users
источник
Я предпочитаю использовать заголовки HTTP для такого рода контекстной информации.
Для общего количества элементов я использую
X-total-count
заголовок.Для ссылок на следующую, предыдущую страницу и т. Д. Я использую
Link
заголовок http :http://www.w3.org/wiki/LinkHeader
Github делает это так же: https://developer.github.com/v3/#pagination
На мой взгляд, он чище, поскольку его можно использовать и при возврате контента, который не поддерживает гиперссылки (например, двоичные файлы, изображения).
источник
X-
.В последнее время я проводил обширные исследования этого и других вопросов, связанных с поиском по REST, и счел целесообразным добавить сюда некоторые из моих выводов. Я немного расширяю этот вопрос, чтобы включить мысли о подкачке страниц, а также подсчитать их, поскольку они тесно связаны между собой.
Заголовки
Метаданные подкачки включены в ответ в виде заголовков ответа. Большим преимуществом этого подхода является то, что сама полезная нагрузка ответа - это именно то, о чем запрашивал фактический запросчик данных. Облегчение обработки ответа для клиентов, которые не заинтересованы в информации подкачки.
Существует несколько (стандартных и пользовательских) заголовков, используемых в джокере для возврата информации, связанной с подкачкой, включая общее количество.
X-Total-Count
Это используется в некоторых API, которые я нашел в дикой природе. Существуют также пакеты NPM для добавления поддержки этого заголовка, например, в Loopback. Некоторые статьи также рекомендуют устанавливать этот заголовок.
Он часто используется в сочетании с
Link
заголовком, который является довольно хорошим решением для подкачки, но в нем отсутствует информация об общем количестве.Ссылка на сайт
Из прочтения этой темы я чувствую, что общий консенсус заключается в том, чтобы использовать
Link
заголовок для предоставления пейджинговых ссылок клиентам, использующимrel=next
иrel=previous
т. Д. Проблема в том, что ему не хватает информации о том, сколько всего записей, что почему многие API объединяют это сX-Total-Count
заголовком.В качестве альтернативы некоторые API и, например, стандарт JsonApi , используют
Link
формат, но добавляют информацию в конверт ответа, а не в заголовок. Это упрощает доступ к метаданным (и создает место для добавления информации об общем количестве) за счет увеличения сложности доступа к самим фактическим данным (путем добавления конверта).Content-Range
При поддержке статьи в блоге под названием Range header я выбираю вас (для нумерации страниц)! , Автор приводит веские аргументы для используя
Range
иContent-Range
заголовков для пагинации. Когда мы внимательно прочитаем в RFC на эти заголовки, мы находим , что расширение их значения за пределы диапазонов байтов фактически порочит RFC и явно разрешены. При использованииitems
вместо контекстаbytes
заголовок Range фактически дает нам возможность как запросить определенный диапазон элементов, так и указать, к какому диапазону общего результата относятся элементы ответа. Этот заголовок также дает отличный способ показать общее количество. И это настоящий стандарт, который в основном отображает один на один для пейджинга. Это также используется в дикой природе .Конверт
Многие API, в том числе и на нашем любимом сайте вопросов и ответов, используют конверт , обертку вокруг данных, которая используется для добавления метаинформации о данных. Кроме того, стандарты OData и JsonApi используют конверт ответа.
Большим недостатком этого (imho) является то, что обработка данных ответа становится более сложной, так как фактические данные должны быть найдены где-то в конверте. Также есть много разных форматов для этого конверта, и вы должны использовать правильный. Это говорит о том, что конверты ответа от OData и JsonApi сильно отличаются друг от друга, причем OData смешивает метаданные в нескольких точках ответа.
Отдельная конечная точка
Я думаю, что это было достаточно освещено в других ответах. Я не исследовал это много, потому что я согласен с комментариями, что это сбивает с толку, поскольку у вас теперь есть несколько типов конечных точек. Я думаю, что было бы лучше, если бы каждая конечная точка представляла (коллекцию) ресурс (ы).
Дальнейшие мысли
Нам нужно не только передавать метаинформацию подкачки, связанную с ответом, но и разрешать клиенту запрашивать определенные страницы / диапазоны. Интересно также взглянуть на этот аспект, чтобы получить согласованное решение. Здесь также мы можем использовать заголовки (
Range
заголовок кажется очень подходящим) или другие механизмы, такие как параметры запроса. Некоторые люди выступают за обработку страниц результатов , как отдельные ресурсы, которые могут иметь смысл в некоторых случаях использования (например/books/231/pages/52
, я в конечном итоге выбор дикого диапазона часто используемых параметры запроса , такие какpagesize
,page[size]
иlimit
т.д. , в дополнении к поддержкеRange
заголовка (и в качестве параметра запроса также).источник
Range
заголовок, однако я не смог найти достаточно доказательств того, что использование чего-либо, кромеbytes
типа диапазона, является допустимым.acceptable-ranges = 1#range-unit | "none"
я думаю, что эта формулировка явно оставляет место для других единиц измерения дальностиbytes
, хотя сама спецификация только определяетbytes
.Альтернатива, когда вам не нужны реальные предметы
Ответ Франци Пенова, безусловно, лучший путь, поэтому вы всегда возвращаете элементы вместе со всеми дополнительными метаданными о запрашиваемых объектах. Так и должно быть.
но иногда возвращать все данные не имеет смысла, потому что они могут вам вообще не понадобиться. Может быть, все, что вам нужно, это метаданные о вашем запрашиваемом ресурсе. Как общее количество или количество страниц или что-то еще. В таком случае вы всегда можете сделать так, чтобы URL-запрос указывал вашей службе не возвращать элементы, а просто метаданные, например:
или что-то подобное ...
источник
metaonly
илиincludeitems
нет.Вы можете вернуть счетчик как пользовательский заголовок HTTP в ответ на запрос HEAD. Таким образом, если клиенту нужен только счет, вам не нужно возвращать фактический список, и нет необходимости в дополнительном URL.
(Или, если вы находитесь в контролируемой среде от конечной точки к конечной точке, вы можете использовать собственный HTTP-глагол, такой как COUNT.)
источник
Я бы порекомендовал добавить заголовки для того же, например:
За подробностями обращайтесь к:
https://github.com/adnan-kamili/rest-api-response-format
Для файла чванства:
https://github.com/adnan-kamili/swagger-response-template
источник
Начиная с "X -" - Префикс устарел. (см .: https://tools.ietf.org/html/rfc6648 )
Мы нашли «Accept-Ranges» как лучшую ставку для отображения разбивки на страницы: https://tools.ietf.org/html/rfc7233#section-2.3 Поскольку «Единицы измерения» могут быть либо «байтами», либо « лексема». Оба не представляют пользовательский тип данных. (см .: https://tools.ietf.org/html/rfc7233#section-4.2 ) Тем не менее, заявлено, что
Что указывает: использование пользовательских единиц измерения диапазона не противоречит протоколу, но МОЖЕТ игнорироваться.
Таким образом, мы должны были бы установить Accept-Ranges на «members» или любой другой тип дальнобойного подразделения, как мы и ожидали. И, кроме того, также установите Content-Range на текущий диапазон. (см .: https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.12 )
В любом случае, я бы придерживался рекомендации RFC7233 ( https://tools.ietf.org/html/rfc7233#page-8 ) для отправки 206 вместо 200:
Таким образом, в результате мы получили бы следующие поля заголовка HTTP:
Для Частичного Содержания:
Для полного содержания:
источник
Кажется, проще всего просто добавить
и вернуть общее количество членов
источник
Как насчет новой конечной точки> / api / members / count, которая просто вызывает Members.Count () и возвращает результат
источник
members
коллекция может быть создана с помощью запроса POST/api
, будет/api/members/count
ли она также создана как побочный эффект, или я должен выполнить явный запрос POST, чтобы создать ее, прежде чем запрашивать ее? :-)Иногда фреймворкам (таким как $ resource / AngularJS) требуется массив в качестве результата запроса, и вы не можете получить ответ, как
{count:10,items:[...]}
в этом случае я храню «count» в responseHeaders.PS На самом деле вы можете сделать это с помощью $ resource / AngularJS, но для этого нужны некоторые настройки.
источник
isArray: false|true
Вы могли бы рассмотреть
counts
как ресурс. URL будет тогда:источник
При запросе разбитых на страницы данных вы знаете (по явному значению параметра размера страницы или значению размера страницы по умолчанию) размер страницы, чтобы вы знали, получили ли вы все данные в ответ или нет. Когда в ответе меньше данных, чем размер страницы, вы получаете целые данные. Когда полная страница возвращается, вы должны снова попросить другую страницу.
Я предпочитаю иметь отдельную конечную точку для подсчета (или такую же конечную точку с параметром countOnly). Потому что вы могли бы подготовить конечного пользователя к длительному / длительному процессу, показав правильно инициированный индикатор выполнения.
Если вы хотите возвращать размер данных в каждом ответе, должен быть указан размер страницы, а также упомянутое смещение. Если честно, лучший способ - это повторить фильтры запросов. Но ответ стал очень сложным. Итак, я предпочитаю выделенную конечную точку для возврата счетчика.
Моя связь, предпочитаю параметр countOnly существующей конечной точке. Таким образом, когда указано, ответ содержит только метаданные.
конечная точка? фильтр = значение
конечная точка? фильтр = значение & countOnly = TRUE
источник