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


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

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

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

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

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

10 95

10 ответов:

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

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

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

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

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

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

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

заголовки

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

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

X-Total-Count

X-Total-Count: 234

используется некоторыеAPIs я нашел в дикой природе. Есть также 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 etc. Проблема в том, что ему не хватает информации о том, сколько всего записей есть, поэтому многие API в сочетании с .

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

Content-Range

Content-Range: items 0-49/234

продвигается статьей в блоге под названием заголовок диапазона, я выбираю вас (для разбиения на страницы)!. Автор приводит веские доводы в пользу использования Range и Content-Range заголовки для разбиения на страницы. Когда мы внимательно читаем theRFC на этих заголовках мы обнаруживаем, что расширение их значения за пределы диапазонов байтов было фактически ожидается RFC и явно разрешено. При использовании в контексте items вместо bytes, заголовок диапазона фактически дает нам способ как запросить определенный диапазон элементов, так и указать, к какому диапазону общего результата относятся элементы ответа. Этот заголовок также дает отличный способ показать общее количество. И это настоящий стандарт, который в основном отображает один к одному на пейджинг. Это тоже используется в дикой природе.

конверт

Многие API, в том числе тот, что с нашего любимого сайта Q&A использовать конверт, оболочка вокруг данных, которая используется для добавления метаинформации о данных. Кроме того,службы и JsonApi стандарты оба используют конверт ответа.

большой недостаток этого (imho) заключается в том, что обработка данных ответа становится более сложной, поскольку фактические данные должны быть найдены где-то в конверте. Также есть много различных форматов для этого конверт и вы должны использовать правильный. Это говорит о том, что конверты ответов от OData и JsonApi сильно отличаются, причем OData смешивается в метаданных в нескольких точках ответа.

отдельная конечная точка

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

дальнейшие мысли

мы не только должны передавать метаинформацию подкачки, связанную с ответом, но также позволять клиенту запрашивать определенные страницы/диапазоны. Интересно также посмотреть на этот аспект, чтобы в конечном итоге получить согласованное решение. Здесь тоже можно использовать заголовки (теги Range заголовок кажется очень подходящим), или другие механизмы, такие как параметры запроса. Некоторые люди выступают за то, чтобы рассматривать страницы результатов как отдельные ресурсов, что может иметь смысл в некоторых случаях (например,/books/231/pages/52. В итоге я выбрал дикий диапазон часто используемых параметров запроса, таких как pagesize,page[size] и limit etc в дополнение к поддержке Range заголовок (и как параметр запроса также).

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

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

альтернатива, когда вам не нужны фактические элементы

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

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

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

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

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

HTTP/1.1 200

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

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

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

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

для файла swagger:

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

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

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

GET
/api/members/count

и возвращает общее количество членов

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

P. S. На самом деле вы можете сделать это с помощью $resource/AngularJS, но для этого нужны некоторые настройки.

при запросе разбитых на страницы данных вы знаете (по значению параметра явного размера страницы или значению размера страницы по умолчанию) размер страницы, поэтому вы знаете, получили ли вы все данные в ответ или нет. Когда в ответе меньше данных, чем размер страницы, тогда вы получили все данные. Когда возвращается полная страница, вы должны снова попросить другую страницу.

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

Если вы хотите вернуть datasize в каждом ответе, должен быть указан размер страницы, смещение. Честно говоря, лучший способ-повторить фильтры запроса тоже. Но ответ стал очень сложным. Поэтому я предпочитаю выделенную конечную точку для возврата count.

<data>
  <originalRequest>
    <filter/>
    <filter/>
  </originalReqeust>
  <totalRecordCount/>
  <pageSize/>
  <offset/>
  <list>
     <item/>
     <item/>
  </list>
</data>

мой Couleage, предпочитает параметр countOnly существующей конечной точке. Итак, когда указан ответ содержит метаданные только.

конечная точка?фильтр=значение

<data>
  <count/>
  <list>
    <item/>
    ...
  </list>
</data>

конечная точка?filter=value & countOnly=true

<data>
  <count/>
  <!-- empty list -->
  <list/>
</data>