Полезная нагрузка для постраничного ответа из API RESTful
Я хочу поддерживать разбивку на страницы в моем RESTful API.
Мой API-метод должен вернуть список продуктов JSON через /products/index
. Тем не менее, есть потенциально тысячи продуктов, и я хочу их просматривать, поэтому моя просьба должна выглядеть примерно так:
/products/index?page_number=5&page_size=20
Но каков должен быть мой ответ JSON? Потребители API обычно ожидают метаданные разбиения на страницы в ответе? Или нужен только набор продуктов? Почему?
Похоже, что API Twitter включает метаданные: https://dev.twitter.com/docs/api/1/get/lists/members (см. Пример запроса).
С метаданными:
{
"page_number": 5,
"page_size": 20,
"total_record_count": 521,
"records": [
{
"id": 1,
"name": "Widget #1"
},
{
"id": 2,
"name": "Widget #2"
},
{
"id": 3,
"name": "Widget #3"
}
]
}
Просто массив продуктов (без метаданных):
[
{
"id": 1,
"name": "Widget #1"
},
{
"id": 2,
"name": "Widget #2"
},
{
"id": 3,
"name": "Widget #3"
}
]
Ответы
Ответ 1
API ReSTful потребляются в основном другими системами, поэтому я помещаю данные подкачки в заголовки ответов. Тем не менее, некоторые пользователи API могут не иметь прямого доступа к заголовкам ответов или могут создавать UX по вашему API, поэтому предоставление способа получения (по запросу) метаданных в ответе JSON является плюсом.
Я полагаю, что ваша реализация должна включать машиночитаемые метаданные в качестве значения по умолчанию и метаданные, доступные для чтения по запросу. Человекочитаемые метаданные могут быть возвращены с каждым запросом, если вам нравится или, желательно, по запросу через параметр запроса, например include=metadata
или include_metadata=true
.
В вашем конкретном сценарии я бы включил URI для каждого продукта с записью. Это позволяет потребителю API создавать ссылки на отдельные продукты. Я также задал бы некоторые разумные ожидания в соответствии с лимитами моих запросов подкачки. Принятие и документирование настроек по умолчанию для размера страницы является приемлемой практикой. Например, GitHub API устанавливает размер страницы по умолчанию в 30 записей с максимум 100, плюс устанавливает ограничение скорости на количество раз, когда вы можете запросить API. Если ваш API имеет размер страницы по умолчанию, строка запроса может просто указывать индекс страницы.
В сценарии, понятном человеку, при переходе на /products?page=5&per_page=20&include=metadata
ответ может быть:
{
"_metadata":
{
"page": 5,
"per_page": 20,
"page_count": 20,
"total_count": 521,
"Links": [
{"self": "/products?page=5&per_page=20"},
{"first": "/products?page=0&per_page=20"},
{"previous": "/products?page=4&per_page=20"},
{"next": "/products?page=6&per_page=20"},
{"last": "/products?page=26&per_page=20"},
]
},
"records": [
{
"id": 1,
"name": "Widget #1",
"uri": "/products/1"
},
{
"id": 2,
"name": "Widget #2",
"uri": "/products/2"
},
{
"id": 3,
"name": "Widget #3",
"uri": "/products/3"
}
]
}
Для машиночитаемых метаданных я бы добавил заголовки ссылок в ответ:
Link: </products?page=5&perPage=20>;rel=self,</products?page=0&perPage=20>;rel=first,</products?page=4&perPage=20>;rel=previous,</products?page=6&perPage=20>;rel=next,</products?page=26&perPage=20>;rel=last
(значение заголовка ссылки должно быть уркокодировано)
... и, возможно, пользовательский заголовок ответа total-count
, если вы так решите:
total-count: 521
Другие данные подкачки, обнаруженные в метаданных, ориентированных на человека, могут быть излишними для машинных метаданных, поскольку заголовки ссылок позволяют мне знать, на какой странице я нахожусь, и число на страницу, и я могу быстро получить количество записей в массиве. Поэтому я бы, вероятно, создал только заголовок для общего счета. Вы всегда можете изменить свое мнение и добавить больше метаданных.
В стороне, вы можете заметить, что я удалил /index
из вашего URI. Общепринятое соглашение заключается в том, чтобы ваша конечная точка ReST выставляла коллекции. Имея /index
в конце муфты, немного вверх.
Это всего лишь несколько вещей, которые мне нравятся при использовании/создании API. Надеюсь, что это поможет!
Ответ 2
Как кто-то, кто написал несколько библиотек для использования сервисов REST, позвольте мне дать вам точку зрения клиента, почему я думаю, что перенос результата в метаданные - это путь:
- Без общего счета, как клиент может знать, что он еще не получил все, что есть, и должен продолжать пейджинг через результирующий набор? В пользовательском интерфейсе, который не выполнял перспективу на следующей странице, в худшем случае это может быть представлено как ссылка Next/More, которая на самом деле не извлекала никаких дополнительных данных.
- Включение метаданных в ответ позволяет клиенту отслеживать меньшее состояние. Теперь мне не нужно сопоставлять свой запрос REST с ответом, так как ответ содержит метаданные, необходимые для восстановления состояния запроса (в данном случае курсора в набор данных).
- Если состояние является частью ответа, я могу одновременно выполнять несколько запросов в один и тот же набор данных, и я могу обрабатывать запросы в любом порядке, в котором они будут поступать, что не обязательно означает, что я сделал запросы.
И предложение: Как Twitter API, вы должны заменить page_number прямым указателем/курсором. Причина в том, что API позволяет клиенту устанавливать размер страницы за запрос. Является ли возвращаемое число_страницы числом страниц, запрошенным клиентом до сих пор, или номером страницы, данным последним используемым page_size (почти наверняка позже, но почему бы вообще не избежать такой неоднозначности)?
Ответ 3
Я бы порекомендовал добавить заголовки для того же. Перемещение метаданных в заголовки помогает избавиться от таких конвертов, как result
, data
или records
а тело ответа содержит только те данные, которые нам нужны. Вы можете использовать заголовок ссылки, если вы тоже генерируете ссылки на страницы.
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
Как правило, я делаю простым способом, независимо от того, что я создаю конечную точку restAPI, например "localhost/api/method/: lastIdObtained/: countDateToReturn" с этими параметрами, вы можете сделать это простым запросом. в сервисе, например. .сеть
jsonData function(lastIdObtained,countDatetoReturn){
'... write your code as you wish..'
and into select query make a filter
select top countDatetoreturn tt.id,tt.desc
from tbANyThing tt
where id > lastIdObtained
order by id
}
В Ionic, когда я прокручиваю снизу вверх, я пропускаю нулевое значение, когда я получаю ответ, я устанавливаю значение последнего полученного идентификатора, и когда я скользлю сверху вниз, я пропускаю последний полученный регистрационный идентификатор