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-адрес используется для идентификации ресурса, а добавленные параметры запроса указывают, какую часть ресурса вы хотите, какое состояние должен иметь ресурс и т. Д.


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 эти ограничения основаны на возможностях клиента (браузер) и сервера (конфигурация).


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


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.


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.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.