REST API Лучшие практики: где разместить параметры? [закрыто]

348

REST API может иметь параметры как минимум двумя способами:

  1. Как часть URL-пути (то есть /api/resource/parametervalue )
  2. В качестве аргумента запроса (то есть /api/resource?parameter=value )

Какова лучшая практика здесь? Существуют ли общие рекомендации, когда использовать 1 и когда использовать 2?

Пример из реальной жизни: Twitter использует параметры запроса для указания интервалов. ( http://api.twitter.com/1/statuses/home_timeline.json?since_id=12345&max_id=54321)

Будет ли лучшим вариантом разместить эти параметры в пути URL?

Калле Густафссон
источник

Ответы:

254

Если есть документированные лучшие практики, я их еще не нашел. Тем не менее, вот несколько рекомендаций, которые я использую при определении того, куда поместить параметры в URL:

Необязательные параметры, как правило, легче вводить в строку запроса.

Если вы хотите вернуть ошибку 404, когда значение параметра не соответствует существующему ресурсу, я бы склонялся к параметру сегмента пути. например/customer/232 где 232 не является действительным идентификатором клиента.

Однако, если вы хотите вернуть пустой список, тогда, когда параметр не найден, я предлагаю использовать параметры строки запроса. например/contacts?name=dave

Если параметр влияет на все поддерево вашего пространства URI, используйте сегмент пути. например, языковой параметр /en/document/foo.txt против/document/foo.txt?language=en

Я предпочитаю, чтобы уникальные идентификаторы были в сегменте пути, а не в параметре запроса.

Официальные правила для URI находятся в этой спецификации RFC здесь . Существует также еще один очень полезный RFC спецификации здесь , что правила определяет для параметрирования URI.

Даррел Миллер
источник
5
Официальные URI правил и черновик проекта были действительно полезными и интересными! :-)
КайМагнус
1
Тест на ошибки 404 очень помогает мне избежать размещения информации в пути, который принадлежит параметрам запроса, заголовкам или телу запроса. Спасибо за это!
Кевин Кондон
152

Поздний ответ, но я добавлю некоторую дополнительную информацию к тому, что было передано, а именно, что есть несколько типов «параметров» для запроса, и вы должны принять это во внимание.

  1. Локаторы - например, идентификаторы ресурсов, такие как идентификаторы или действие / представление
  2. Фильтры - например, параметры, которые обеспечивают поиск, сортировку или сужение набора результатов.
  3. Состояние - например, идентификация сессии, ключи API, whatevs.
  4. Содержание - например, данные для хранения.

Теперь давайте посмотрим на различные места, где эти параметры могут идти.

  1. Запросить заголовки и куки
  2. Строка запроса URL («GET»)
  3. URL-пути
  4. Строка запроса тела / multipart (переменные "POST")

Как правило, вы хотите, чтобы State был установлен в заголовках или файлах cookie, в зависимости от того, какой тип информации о состоянии это. Я думаю, что мы все можем согласиться с этим. Используйте пользовательские заголовки http (X-My-Header), если вам нужно.

Аналогично, у контента есть только одно место, которое должно принадлежать, которое находится в теле запроса, либо в виде строк запроса, либо в виде содержимого http multipart и / или JSON. Это соответствует тому, что вы получаете от сервера, когда он отправляет вам контент. Так что не стоит грубить и поступать иначе.

Такие локаторы, как «id = 5» или «action = refresh» или «page = 2», имеют смысл иметь в качестве URL-пути, например, mysite.com/article/5/page=2когда вы частично знаете, что должна означать каждая часть (основы, такие как article и 5 очевидно означает получение данных типа article с идентификатором 5), а дополнительные параметры указываются как часть URI. Они могут быть в форме page=2или, page/2если вы знаете, что после определенной точки в URI «папки» являются парными значениями ключей.

Фильтры всегда идут в строке запроса, потому что, хотя они являются частью поиска правильных данных, они только там, чтобы вернуть подмножество или модификацию того, что локаторы возвращают в одиночку. Поиск в mysite.com/article/?query=Obama(подмножестве) является фильтром, как и /article/5?order=backwards(модификация). Подумайте о том, что он делает, а не только как это называется!

Если «view» определяет формат вывода, то это filter ( mysite.com/article/5?view=pdf), потому что оно возвращает модификацию найденного ресурса, а не поиск того, какой ресурс мы хотим. Если вместо этого он решает, какую конкретную часть статьи мы увидим ( mysite.com/article/5/view=summary), то это локатор.

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

Надеюсь, что это помогло людям подарить несколько моментов Эврика, если они потеряли из-за того, куда положить вещи!

Тор Валамо
источник
2
Почему тогда не idфильтр? Возвращает подмножество ресурса
Джонатан.
13
@Джонатан. Нет, он возвращает конкретный ресурс, а именно статью № 5. Фильтр - это всегда способ сузить поиск в коллекции ресурсов. Если вы хотите именно этот конкретный ресурс, то должен быть назначен способ получить это. Фильтрация означает, что у вас есть возможность вернуть несколько ресурсов. Идентификатор - это не фильтр, это определенный отдельный ресурс. Если бы у вас был ДИАПАЗОН идентификаторов, то это был бы фильтр, даже если в диапазон был включен только один идентификатор. Если фильтр также включает типы ресурсов, он вернет все ресурсы с идентификатором 5, а не только статью.
Тор Валамо
1
@Jonathan .: как упоминалось в DarrelMiller, вы ожидаете, что запрос объекта / идентификатора вернет 404 в случае неизвестного идентификатора, в то время как вы ожидаете, что объект? Id = id вернет и пустой список. Кроме того, я считаю, что любой тип фильтрации / поднабора должен возвращать список.
njzk2
1
Страницы сложны, потому что, как вы говорите, это может быть фильтр ресурса (коллекции страниц), но в то же время это конкретный ресурс в этой коллекции. Я бы всегда запрашивал страницу статьи по локатору, а не по фильтру. Однако страница может быть фильтром списка чего-либо, скажем, списка пользователей. Но тогда страница по своей сути является разделителем, то есть «начать с элемента (page-1)*perpageи показать perpageэлементы». Правильно использовать его в качестве фильтра, но по разным причинам. Называть это «страницей» технически неправильно. Более семантически правильным было бы назвать его «from» или «startAt»
Tor Valamo
1
(продолжение) Семантическое значение «страницы» заключается в том, что это конкретный ресурс, который не меняется. Это происходит от физической печати. Если бы у нас никогда не было книг или печатных материалов, «страница» не была бы словом. Если у вас есть динамический список элементов, разбитый на «страницы», вы должны указать конкретную отправную точку, числовую, алфавитную или даже для конкретного элемента, а также фильтр «сколько на страницу». Если я хочу сослаться на что-то в вашем списке, я хочу конкретику. Кроме того, я не хочу переходить на страницу 5 только для того, чтобы понять, что вы изменили внутреннее значение perpageна 50 вместо 20.
Tor Valamo
21

Это зависит от дизайна. Нет никаких правил для URI в REST через HTTP (главное, чтобы они были уникальными). Часто дело доходит до вкуса и интуиции ...

Я использую следующий подход:

  • url path-element: ресурс и его path-элемент образуют обход каталога и подресурс (например, / items / {id}, / users / items). Если вы не уверены, спросите своих коллег, считают ли они этот обход и думают ли они в «другом каталоге», что наиболее вероятным является path-element.
  • Параметр url: когда на самом деле обхода нет (поисковые ресурсы с несколькими параметрами запроса являются очень хорошим примером для этого)
Мануэль Алдана
источник
1
На самом деле существуют довольно четкие правила того, как должен выглядеть URI, и очень небольшая двусмысленность в том, как применять их к RESTful URI.
DanMan
18

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

PeterWong
источник
7
На самом деле, и путь, и запрос используются в комбинации для идентификации ресурса. Это было разъяснено в RFC 3986 http://labs.apache.org/webarch/uri/rfc/rfc3986.html#query
Даррел Миллер
@DarrelMiller Я знаю, что это старый пост, но мне интересно узнать больше о параметрах запроса фактов, которые также используются для идентификации ресурса. Указанная вами ссылка теперь не работает. Я посмотрел на RFC3986, но не понимаю, как вы поняли этот факт. Кроме того, по определению параметры идентификатора не должны быть необязательными, поэтому не представляется целесообразным использовать параметры запроса для идентификации.
Микаэль Маррач
@MickaelMarrache См. Первую строку в разделе 3.4 tools.ietf.org/html/rfc3986#section-3.4
Даррел Миллер
2
@DarrelMiller Спасибо! Мой вопрос связан с тем фактом, что обычно промежуточные компоненты HTTP не кэшируют ответы на запросы, содержащие строку запроса. Таким образом, кажется, что параметры запроса больше соответствуют ресурсам поиска в соответствии с некоторыми критериями, а не для однозначной идентификации ресурса.
Микаэль Маррач
17

Согласно реализации REST,

1) Переменные пути используются для прямого действия над ресурсами, например, контакт или песня, например.
GET и т.д. / api / resource / {songid} или
GET и т.д. / api / resource / {contactid} будут возвращать соответствующие данные.

2) Запрос perms / аргумент используется для косвенных ресурсов, таких как метаданные песни ex., GET / api / resource / {songid}? Metadata = genres, он возвращает данные жанров для этой конкретной песни.

Сатиш Беллапу
источник
5
На самом деле нет стандарта REST . В Википедии : В отличие от веб-сервисов на основе SOAP, не существует «официального» стандарта для веб-API RESTful. [14] Это потому, что REST - это архитектурный стиль, в отличие от SOAP, который является протоколом. Хотя REST не является стандартом, реализация RESTful, такая как Web, может использовать такие стандарты, как HTTP, URI, XML и т. Д.
DavidRR
Мне не нравится 2 подход. Я предпочел бы предпочесть / api / genres? Songid = 123 или / api / songs / {song-id} / genres
Барт Каликсто
1
@Bart, Satish имел в виду переменные в пути, что, по сути, является тем, на что вы ссылались как на ваши предпочтения ... однако, если жанры на самом деле являются метаданными, а не полем сущности / ресурса песни ... тогда я мог бы видеть больше чувствительности в использовании строки запроса на это ..
Бретт Касвелл
@BrettCaswell получил это! спасибо за указание на это. действительно ценю это!
Барт Каликсто
16

«Упакуйте» и отправьте ваши данные в «контекст», который предоставляет юниверс-ресурс-локатор, что означает № 1 ради локатора.

Имейте в виду ограничения с # 2. Я предпочитаю посты № 1.

примечание: ограничения обсуждаются для

POST in Существует ли максимальный размер содержимого параметра POST?

GET in Есть ли ограничение на длину запроса GET? и максимальный размер параметров URL в _GET

ps эти ограничения основаны на возможностях клиента (браузер) и сервера (конфигурация).

DGM
источник
дополнение: остроумные маршруты могут иметь версии (различаемые через заголовки), что обеспечивает развитую функциональность без необходимости изменять код, который использует код полного остального (api), который вы пишете, как в restify ->
looked Route
5

В соответствии со стандартом URI путь предназначен для иерархических параметров, а запрос - для неиерархических параметров. Ofc. это может быть очень субъективным, что является иерархическим для вас.

В ситуациях, когда несколько URI назначены одному и тому же ресурсу, я хотел бы поместить параметры - необходимые для идентификации - в путь, а параметры - необходимые для построения представления - в запрос. (Для меня так легче проложить маршрут.)

Например:

  • /users/123 и /users/123?fields="name, age"
  • /users и /users?name="John"&age=30

Для уменьшения карты мне нравится использовать следующие подходы:

  • /users?name="John"&age=30
  • /users/name:John/age:30

Так что это действительно зависит от вас (и вашего маршрутизатора на стороне сервера), как вы создаете свои URI.

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

inf3rno
источник
4

Как программист часто на стороне клиента, я предпочитаю аргумент запроса. Кроме того, для меня он отделяет путь URL-адреса от параметров, добавляет ясности и обеспечивает большую расширяемость. Это также позволяет мне иметь отдельную логику между построением URL / URI и построителем параметров.

Мне нравится то, что сказал Мануэль Алдана о другом варианте, если там задействовано какое-то дерево. Я могу видеть, как специфичные для пользователя части обрабатываются таким образом.

Джо Планте
источник
4

Не существует жестких и быстрых правил, но практическое правило с чисто концептуальной точки зрения, которое я хотел бы использовать, можно кратко суммировать следующим образом: путь URI (по определению) представляет ресурс, а параметры запроса по существу являются модификаторами этого ресурса. , До сих пор , что , вероятно , не поможет ... С REST API у вас есть основные методы воздействуя на один ресурс , используя GET, PUTи DELETE. Следовательно, следует ли что-то представлять в пути или в качестве параметра, можно свести к тому, имеют ли эти методы смысл для рассматриваемого представления. Будете ли вы разумно PUTчто-то делать на этом пути и будет ли это семантически обоснованным? Можно конечноPUT что-то где угодно и согнуть бэкэнд, чтобы справиться с этим, но вы должны бытьPUTчто представляет собой представление фактического ресурса, а не какую-то излишне контекстуализированную его версию. Для коллекций то же самое можно сделать с POST. Если вы хотите добавить в определенную коллекцию, какой URL имеет смысл POSTдля.

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

В ответ на реальный пример, приведенный в исходном вопросе (API Twitter), параметры представляют собой транзитивный запрос, который фильтрует состояние ресурсов (а не иерархию). В этом конкретном примере было бы совершенно неразумно добавлять в коллекцию, представленную этими ограничениями, и, кроме того, этот запрос не мог бы быть представлен в виде пути, который имел бы какой-либо смысл в терминах графа объектов.

Принятие этого типа ресурс-ориентированной перспективы может легко отобразиться непосредственно на граф объектов вашей доменной модели и довести логику вашего API до такой степени, что все работает очень чисто и довольно самодокументирующимся образом, как только он становится ясным. Эта концепция также может быть прояснена путем отказа от систем, использующих традиционную маршрутизацию URL-адресов, сопоставленных с обычно плохо подходящей моделью данных (т. Е. СУБД). Apache Sling , безусловно, будет хорошим началом. Концепция диспетчеризации обхода объекта в такой системе, как Zope, также дает более четкий аналог.

Мэтт Уиппл
источник
4

Вот мое мнение.

Параметры запроса используются в качестве метаданных для запроса. Они действуют как фильтр или модификатор для существующего вызова ресурса.

Пример:

/calendar/2014-08-08/events

должен дать календарь событий на этот день.

Если вы хотите события для определенной категории

/calendar/2014-08-08/events?category=appointments

или если вам нужны события более 30 минут

/calendar/2014-08-08/events?duration=30

Лакмусовый тест должен был бы проверить, может ли запрос все еще быть обслужен без параметров запроса.

сойка
источник
2

Я обычно склоняюсь к # 2, в качестве аргумента запроса (т.е. / api / resource? Parameter = value).

Третий вариант - фактически разместить параметр = значение в теле.

Это потому, что он лучше работает для многопараметрических ресурсов и более расширяем для будущего использования.

Независимо от того, какой вы выберете, убедитесь, что вы выбрали только один, не смешивайте и не сочетайте. Это приводит к запутанному API.

NorthIsUp
источник
2

Одно «измерение» этой темы было опущено, но оно очень важно: бывают случаи, когда «лучшие практики» должны согласовываться с платформой, которую мы реализуем или дополняем возможностями REST.

Практический пример:

В настоящее время многие веб-приложения реализуют архитектуру MVC (модель, представление, контроллер). Они предполагают, что указан определенный стандартный путь, особенно если эти веб-приложения поставляются с опцией «Включить SEO URL».

Просто упомяну довольно известное веб-приложение: интернет-магазин OpenCart. Когда администратор включает «SEO URL-адреса», он ожидает, что указанные URL-адреса приходят в довольно стандартном формате MVC, например:

http://www.domain.tld/special-offers/list-all?limit=25

куда

  • special-offers является контроллером MVC, который должен обрабатывать URL (показывая страницу специальных предложений)

  • list-allявляется действием контроллера или именем функции для вызова. (*)

  • Параметр limit = 25 указывает, что на странице будет отображаться 25 элементов.

(*) list-all- это вымышленное имя функции, которое я использовал для ясности. В действительности, OpenCart и большинство платформ MVC имеют функцию по умолчанию, подразумеваемую (и обычно пропускаемую в URL), indexкоторая вызывается, когда пользователь хочет выполнить действие по умолчанию. Таким образом, реальный URL будет:

http://www.domain.tld/special-offers?limit=25

С теперь достаточно стандартным приложением или структурированной структурой, аналогичной приведенной выше, вы часто получаете оптимизированный для него веб-сервер, который переписывает URL-адреса для него (истинный «не SEO-URL» будет выглядеть так: http://www.domain.tld/index.php?route=special-offers/list-all&limit=25 .

Поэтому вы, как разработчик, сталкиваетесь с существующей инфраструктурой и адаптируете свои «лучшие практики», если вы не являетесь системным администратором, точно знаете, как настроить конфигурацию переписывания Apache / NGinx (последняя может быть неприятной!), И так на.

Таким образом, ваш REST API часто будет намного лучше в соответствии со стандартами ссылающегося веб-приложения, как с точки зрения его согласованности, так и с точки зрения простоты / скорости (и, следовательно, экономии бюджета).

Чтобы вернуться к практическому примеру выше, непротиворечивый REST API был бы чем-то вроде URL:

http://www.domain.tld/api/special-offers-list?from=15&limit=25

или (не SEO URL)

http://www.domain.tld/index.php?route=api/special-offers-list?from=15&limit=25

со смесью аргументов "пути сформированы" и аргументов "запроса сформированы".

Дарио Фумагалли
источник
1

Я вижу много REST API, которые плохо обрабатывают параметры. Один из примеров, который часто встречается, - это когда URI содержит информацию, позволяющую установить личность.

http://software.danielwatrous.com/design-principles-for-rest-apis/

Я думаю, что следующий вопрос заключается в том, когда параметр вообще не должен быть параметром, а должен быть перемещен в HEADER или BODY запроса.

Дэниел Уотроус
источник
0

Это очень интересный вопрос.

Вы можете использовать оба из них, здесь нет строгих правил, но использование переменных пути URI имеет некоторые преимущества:

  • Кэш : большинство служб веб-кэширования в Интернете не кэшируют GET-запрос, если они содержат параметры запроса. Они делают это потому, что многие RPC-системы используют GET-запросы для изменения данных на сервере (сбой !! Get должен быть безопасным методом)

Но если вы используете переменные пути, все эти сервисы могут кэшировать ваши запросы GET.

  • Иерархия : переменные пути могут представлять иерархию: / Город / Улица / Место

Это дает пользователю больше информации о структуре данных.

Но если ваши данные не имеют отношения иерархии, вы все равно можете использовать переменные Path, используя запятую или точку с запятой:

/ Город / долгота, широта

Как правило, используйте запятую, когда порядок параметров имеет значение, используйте точку с запятой, когда порядок не имеет значения:

/ IconGenerator / красный, синий, зеленый

Помимо этих причин, в некоторых случаях очень часто используются переменные строки запроса:

  • Когда вам нужно, чтобы браузер автоматически помещал переменные формы HTML в URI
  • Когда вы имеете дело с алгоритмом. Например, движок Google использует строки запроса:

http: // www.google.com/search?q=rest

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

jfcorugedo
источник