Конвенция для объединения запросов и ответов REST?
Для сложного веб-приложения для просмотра одной страницы могут потребоваться данные многих разных типов. Если вы делаете шаблоны на стороне клиента и извлекаете эти данные из веб-службы RESTful, вам может потребоваться много запросов на заполнение страницы. Существует ли какое-либо соглашение, по которому могут быть объединены запросы REST, чтобы получить обратно одну полезную нагрузку, содержащую все необходимые ответы?
Для использования страницы пользователя Stack Exchange в качестве примера вам, вероятно, потребуется позвонить по крайней мере из API-интерфейса стека, чтобы создать страницу:
- /пользователи/{идентификатор}
- /пользователи/{идентификатор}/ответы
- /пользователи/{идентификатор}/репутации
- /пользователи/{идентификатор}/теги
- /пользователи/{ID}/вопросы
- ... и т.д.
Может ли поставщик такого API предоставить вам один запрос для получения всех этих данных? Особенно, если речь идет о других типах корней, скажем, вы также должны были запросить/ревизии/{id} и /tags для сборки страницы по какой-либо причине.
Чтобы быть ясным, я не хочу знать, возможно ли предоставить такую функциональность в веб-API - я хочу знать, есть ли стандартное или, по крайней мере, документированное средство для этого.
Ответы
Ответ 1
Я думаю, нам нужен стандартный способ сделать это, но до тех пор вы, вероятно, сами по себе.
В прошлом у меня были аналогичные проблемы, когда я хотел делать транзакции, распределенные по нескольким ресурсам. Я изобрел новые ресурсы, в которых описывались действия, которые я собирался сделать более четко (вычитание 5 из учетной записи A и добавление 5 в учетную запись B становится транзакцией с членами и членами).
Возможно, подобный (но более общий) подход может быть применен здесь, используя другой вызов REST в качестве контейнера;
POST /batchRequests { requests: [
{ method: 'POST', url: '/resource', payload: { ... } }
], transactional: false }
Что, в свою очередь, возвращает объект BatchRequest, содержащий результаты. Это довольно близко к тому, что сделал Facebook.
Ответ 2
Я думаю, что это может быть против принципов REST, нет? то есть уникальный URI для представления объекта и его полезной нагрузки. Разве это не ухудшит ситуацию? Учитывая, что это не TI-калькулятор, а современный компьютер, способный параллельным HTTP-запросам, стихи один, который займет столько же времени, сколько и их всех.
Не думайте, что есть стандарт, но вот пример - https://developers.facebook.com/docs/reference/api/batch/
Ответ 3
Я знаю, что я очень опаздываю, но, может быть, мои мысли могут помочь другим...
Мое решение немного другое.
Я храню ресурсы в одном uri (унифицированный идентификатор ресурса) на ресурс.
Пользователь
GET /user => INDEX
PUT /user => CREATE (RETURN LOCATION HEADER)
GET /user/{id} => FIND
POST /user/{id} => UPDATE
DELETE /user/{id} => REMOVE
Сообщения
GET /user/{id}/message => INDEX
PUT /user/{id}/message => CREATE (RETURN LOCATION HEADER)
GET /user/{id}/message => FIND
POST /user/{id}/message/{id} => UPDATE
DELETE /user/{id}/message/{id} => REMOVE
Друзья
GET /user/{id}/friend => INDEX
PUT /user/{id}/friend => CREATE (RETURN LOCATION HEADER)
GET /user/{id}/friend => FIND
POST /user/{id}/friend/{id} => UPDATE
DELETE /user/{id}/friend/{id} => REMOVE
Полученный тип контента может быть изменен с помощью HTTP-заголовка, который необходимо указать клиенту.
Accept: "application/vnd.com.example.api+json; version: 1.0;"
В настоящее время это поле Accept HTTP-Header, которое сообщает серверу, какая версия и какой тип содержимого использовать.
Чтобы сохранить функциональность браузера или прокси-кеша, я решил вернуть поле Vary HTTP Header на моем api, со всеми изменяющими HTTP-заголовками в нем, поэтому что браузеры могут корректно отличаться между ресурсами, uri и заголовками.
Vary: "Accept, Accept-Language"
Самая большая проблема заключается в том, как объединить ресурсы, чтобы предотвратить одновременное открытие слишком большого количества подключений tcp.
В качестве примера: если вы запросите список пользователей из моего API через /user, API вернет массив пользовательских объектов в желаемом типе контента (JSON, XML, HTML).
Я не буду вдаваться в подробности, так как есть доступ к страницам и поддержка фильтра/поиска.
Каждый из пользовательских объектов имеет зависимые ресурсы, такие как друзья и/или сообщения.
Существует очень хороший структурированный стандарт под названием HAL (язык приложений гипертекста), который предоставляет несколько простых способов объединения ресурса с зависимыми ресурсами или связанными ресурсами. Он использует JSON для представления данных.
Посмотрите http://stateless.co/hal_specification.html Майк Келли.
Но это решение также не объединяет ресурсы в один вызов, оно просто отправляет uri зависимого или связанного ресурса с полезной нагрузкой клиенту.
Это позволит клиенту решить, какую дополнительную информацию показывать.
И, конечно же, все функции кэширования, которые очень важны, работают.
Большая часть трафика при просмотре ресурсов будет дублировать запросы, которые можно кэшировать очень легко.
Если в приведенном примере указывается количество друзей или количество сообщений пользователя в списке пользователей, подумайте о добавлении зависимого ресурса в качестве ключа к вашему результату.
Пример ответа пользователя (JSON)
{
id: 1,
email: [email protected],
...
friends: {
count: 1,
_links: [{
'id': '2',
'name': 'Peter Parker',
'uri': '//api.example.com/user/2'
}]
},
messages: {
count: 1,
_links: [{
'id': 'x4gZ6',
'subject': 'Testmessage',
'uri': '//api.example.com/user/1/message/x4gZ6'
}]
},
...
}
Теперь расскажите о том, как предотвратить использование нескольких TCP-соединений!
Для этого есть очень простой и хороший подход...
Почему бы не использовать:
Connection: "keep-alive"
Это даст клиенту возможность запросить как можно больше ресурсов одновременно.
Хотя все ресурсы запрашиваются через одно TCP-соединение с сервером.
Я знаю, что существуют другие проблемы с подключением keep-alive, поэтому не стесняйтесь обсуждать со мной все идеи.
Спасибо!
Ответ 4
Я очень опаздываю на эту вечеринку, но я искал освещенный онлайн по теме балансирования чистого дизайна REST и реалий реального веб-сайта. Я нашел обычное "сокращение HTTP-запросов на производительность", но никаких хороших эссе о хороших практиках с прицелом на компромиссы. (Комментарители не стесняются указывать мне направление!) И что, черт возьми, еще нет заметных ответов.
Не уверен, спрашиваете ли вы, возможно ли другим людям сделать вашу жизнь проще, делая это, или вы спрашиваете, потому что вы разрабатываете API. В вашем вопросе говорят другие люди, поэтому мы идем.
Может ли поставщик такого API предоставить вам один запрос для получения всех этих данных? Особенно, если речь идет о других типах корней, скажем, вы также должны были запросить/ревизии/{id} и /tags для сборки страницы по какой-либо причине.
Чтобы быть ясным, я не хочу знать, возможно ли предоставить такую функциональность в веб-API - я хочу знать, есть ли стандартное или, по крайней мере, документированное средство для этого.
Краткая версия: я не видел много хороших эссе по этому вопросу. Популярность REST может быть молодой, но для хорошего академического набора ответов. Где я могу это избежать, я делаю то, что должен сделать, и меньше беспокоюсь о том, насколько чистым является мой дизайн: аббревиатура, подобная REST. Но я бы, вероятно, ошибся в реализации более чистой стандартной (опять же, например, REST) реализации, если бы я был поставщиком большого публичного API (см. Ниже).
Это возможно, и я упустил много чистого дизайна REST в своих приложениях, чтобы достичь того, что мне нужно с точки зрения производительности. В основном RPC или объединение ресурсов получает/обновляет, чтобы свести к минимуму количество HTTP-запросов, во что-то может появиться современный веб-разработчик Frankenstein.
Может ли провайдер такого API предоставить вам один запрос для получения всех этих данных?
Технически он/она, конечно, может это сделать. Но если бы я был издателем популярного публично открытого API, я бы, вероятно, использовал более "чистый" подход REST, потому что простота, удобство и обслуживание теперь приобретают совершенно новое значение, если многие незнакомые люди будут его использовать. Производительность для меня и для потребителей может быть более низким приоритетом в этом случае, чем для корпоративного приложения, для которого я являюсь как потребителем, так и поставщиком.
Как обычно, разработчик должен решить соответствующий баланс между скоростью реализации, чистым дизайном, ремонтопригодностью, производительностью и другими миллионами вещей. (Это решение уже началось с выбора языка - Python/Ruby vs Java/С# vs С++ vs...)