Какой лучший метод RESTful для возврата общего количества элементов в объекте? - PullRequest
118 голосов
/ 15 сентября 2010

Я разрабатываю службу REST API для большого сайта социальной сети, в котором я участвую. Пока что он работает отлично.Я могу выдавать запросы GET, POST, PUT и DELETE на URL-адреса объектов и влиять на мои данные.Тем не менее, эти данные разбиты на страницы (ограничено 30 результатами за раз).

Однако, как лучше всего получить RESTful-общее число, скажем, членов, через мой API?

В настоящее время я отправляю запросы к структуре URL, например:

  • / api / members - возвращает список участников (30 одновременно, как указано выше)
  • / api / members / 1 - Влияет на одного члена в зависимости от используемого метода запроса

Мой вопрос: как мне тогда использовать аналогичную структуру URL дляполучить общее количество участников в моей заявке?Очевидно, что запрос только поля id (аналогично API Graph в Facebook) и подсчет результатов будут неэффективными, поскольку будет возвращен только фрагмент из 30 результатов.

Ответы [ 11 ]

76 голосов
/ 15 сентября 2010

Хотя ответ на / API / users разбит на страницы и возвращает только 30 записей, ничто не мешает вам включить в ответ также общее количество записей и другую соответствующую информацию, такую ​​как размер страницы, номер страницы /смещение и т. д.

API StackOverflow является хорошим примером того же дизайна.Вот документация для метода Users - https://api.stackexchange.com/docs/users

60 голосов
/ 05 сентября 2015

Я предпочитаю использовать заголовки HTTP для этого вида контекстной информации.

Для общего количества элементов я использую заголовок X-total-count.
Для ссылок на следующую, предыдущую страницу и т. Д. Я использую http Link header:
http://www.w3.org/wiki/LinkHeader

Github делает то же самое: https://developer.github.com/v3/#pagination

На мой взгляд, он чище, так как его можно использовать и при возврате контента, который не поддерживаетгиперссылки (т.е. двоичные файлы, изображения).

53 голосов
/ 14 мая 2017

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

Заголовки

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

Существует несколько (стандартных и пользовательских) заголовков, используемых в wild для возврата информации, относящейся к подкачке, включая общее количество.

X-Total-Count

X-Total-Count: 234

Используется в некоторых API , которые я нашел в дикой природе. Есть также пакеты NPM для добавления поддержки этого заголовка, например. Loopback. Некоторые статьи рекомендуют также установить этот заголовок.

Часто используется в сочетании с заголовком Link, который является довольно хорошим решением для подкачки, но в нем отсутствует информация об общем количестве.

Ссылка

Link: </TheBook/chapter2>;
      rel="previous"; title*=UTF-8'de'letztes%20Kapitel,
      </TheBook/chapter4>;
      rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel

Из прочтения этой темы я чувствую, что общий консенсус заключается в использовании Link заголовка для предоставления пейджинговых ссылок клиентам, использующим rel=next, rel=previous и т. Д. Проблема Это связано с тем, что ему не хватает информации о количестве записей, поэтому многие API сочетают это с заголовком X-Total-Count.

В качестве альтернативы, некоторые API и, например, стандарт JsonApi , используйте формат Link, но добавляйте информацию в конверте ответа, а не в заголовок. Это упрощает доступ к метаданным (и создает место для добавления информации об общем количестве) за счет усложнения доступа к самим фактическим данным (путем добавления конверта).

Content-Range

Content-Range: items 0-49/234

Продвигается статья блога с именем Заголовок диапазона, я выбираю вас (для нумерации страниц)! . Автор приводит веские аргументы в пользу использования заголовков 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 (и в качестве параметра запроса).

22 голосов
/ 15 сентября 2010

В ответ на запрос HEAD вы можете вернуть счетчик в виде настраиваемого HTTP-заголовка. Таким образом, если клиенту нужен только счетчик, вам не нужно возвращать фактический список, и нет необходимости в дополнительном URL.

(Или, если вы находитесь в контролируемой среде от конечной точки к конечной точке, вы можете использовать пользовательский HTTP-глагол, такой как COUNT.)

21 голосов
/ 05 января 2015

Альтернатива, когда вам не нужны настоящие предметы

Ответ Франциска Пенова , безусловно, лучший путь, поэтому вы всегда возвращаете элементы вместе со всеми дополнительными метаданными о запрашиваемых ваших объектах. Так и должно быть.

но иногда возвращать все данные не имеет смысла, потому что они могут вам вообще не понадобиться. Может быть, все, что вам нужно, это метаданные о запрашиваемом ресурсе. Как общее количество или количество страниц или что-то еще. В таком случае вы всегда можете попросить URL-адрес указать службе не возвращать элементы, а просто метаданные, например:

/api/members?metaonly=true
/api/members?includeitems=0

или что-то подобное ...

11 голосов
/ 02 августа 2016

Я бы порекомендовал добавить заголовки для того же самого, например:

HTTP/1.1 200

Pagination-Count: 100
Pagination-Page: 5
Pagination-Limit: 20
Content-Type: application/json

[
  {
    "id": 10,
    "name": "shirt",
    "color": "red",
    "price": "$23"
  },
  {
    "id": 11,
    "name": "shirt",
    "color": "blue",
    "price": "$25"
  }
]

Подробнее см.

https://github.com/adnan-kamili/rest-api-response-format

Для файла чванства:

https://github.com/adnan-kamili/swagger-response-template

4 голосов
/ 04 марта 2019

По состоянию на «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) Тем не менее, заявлено, что

Реализации HTTP / 1.1 МОГУТ игнорировать диапазоны, указанные с использованием других ед.

Что означает: использование пользовательских единиц измерения диапазона не противоречит протоколу, но МОЖЕТ игнорироваться.

Таким образом, мы должны установить для 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:

Если все предварительные условия выполняются, сервер поддерживает диапазон
поле заголовка для целевого ресурса и указанные диапазоны:
действительный и выполнимый (как определено в разделе 2.1), сервер ДОЛЖЕН
отправить ответ 206 (Частичное содержимое) с полезной нагрузкой, содержащей один
или более частичные представления, которые соответствуют выполнимым
Запрашиваемые диапазоны, как определено в Разделе 4.

Таким образом, в результате мы получили бы следующие поля заголовка HTTP:

Для частичного содержимого:

206 Partial Content
Accept-Ranges: members
Content-Range: members 0-20/100

Для полного содержания:

200 OK
Accept-Ranges: members
Content-Range: members 0-20/20
2 голосов
/ 16 декабря 2013

Иногда фреймворкам (таким как $ resource / AngularJS) требуется массив в качестве результата запроса, и вы не можете получить ответ типа {count:10,items:[...]}, в этом случае я сохраняю "count" в responseHeaders.PS На самом деле вы можете сделать это с помощью $ resource / AngularJS, но для этого нужны некоторые настройки.

2 голосов
/ 15 сентября 2010

Кажется, проще всего просто добавить

GET
/api/members/count

и вернуть общее количество членов

2 голосов
/ 15 сентября 2010

Как насчет новой конечной точки> / api / members / count, которая просто вызывает Members.Count () и возвращает результат

...