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

365

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

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

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

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

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

Теги:
rest
url
design

14 ответов

266

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

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

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

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

  • Запросить заголовки и файлы cookie
  • Строка запроса URL ( "GET" )
  • URL-адреса
  • Строка запроса тела /multipart ( "POST" )

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

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

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

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

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

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

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

  • 2
    Почему тогда я не id фильтром? Возвращает подмножество ресурса
  • 13
    @Джонатан. Нет, он возвращает конкретный ресурс, а именно номер статьи 5. Фильтр - это всегда способ сузить поиск в коллекции ресурсов. Если вы хотите именно этот конкретный ресурс, то должен быть назначен способ получить это. Фильтрация означает, что у вас есть возможность вернуть несколько ресурсов. Идентификатор - это не фильтр, это определенный отдельный ресурс. Если бы у вас был ряд идентификаторов, то это был бы фильтр, даже если в диапазон был включен только один идентификатор. Если фильтр также включает типы ресурсов, он вернет все ресурсы с идентификатором 5, а не только статью.
Показать ещё 7 комментариев
20

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

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

  • URL-адрес: ресурс и его элемент пути формируют обход каталога и подресурс (например,/items/{id},/users/items). Когда вы не уверены, спросите своих коллег, если они думают, что обход, и они думают в "другом каталоге", наиболее вероятным элементом пути является правильный выбор.
  • Параметр url: при отсутствии обхода (ресурсы поиска с несколькими параметрами запроса являются очень хорошим примером для этого)
  • 1
    На самом деле существуют довольно четкие правила того, как должен выглядеть URI, и очень небольшая двусмысленность в том, как применять их к RESTful URI.
18

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

  • 7
    На самом деле, и путь, и запрос используются в комбинации для идентификации ресурса. Это было разъяснено в RFC 3986 http://labs.apache.org/webarch/uri/rfc/rfc3986.html#query
  • 0
    @DarrelMiller Я знаю, что это старый пост, но мне интересно узнать больше о параметрах запроса фактов, которые также используются для идентификации ресурса. Указанная вами ссылка теперь не работает. Я посмотрел на RFC3986, но не понимаю, как вы поняли этот факт. Кроме того, по определению параметры идентификатора не должны быть необязательными, поэтому не представляется целесообразным использовать параметры запроса для идентификации.
Показать ещё 2 комментария
17

В соответствии с реализацией REST,

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

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

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

"Pack" и POST ваши данные против "контекста", который предоставляет юниверс-ресурс-локатор, что означает №1 для локатора.

Обратите внимание на ограничения С# 2. Я предпочитаю POSTs для # 1.

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

POST в Есть ли максимальный размер для содержимого параметра POST?

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

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

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

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

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

Пример:

/calendar/2014-08-08/events

должен предоставить события календаря для этого дня.

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

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

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

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

Тест лакмусовой бумажки будет проверять, может ли запрос обслуживаться без параметров запроса.

4

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

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

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

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

4

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

Мне нравится то, что сказал мануэль альдана о другом варианте, если есть какое-то дерево. Я вижу, что детали, зависящие от пользователя, выглядят так.

2

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

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

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

Просто упомянем довольно известное веб-приложение: магазин электронной коммерции OpenCart. Когда администратор разрешает "URL-адреса SEO", он ожидает, что указанные 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 имеют функцию default, подразумеваемую (и обычно опущенную в URL) index, которая вызывается, когда пользователь хочет выполнить действие по умолчанию. Таким образом, URL реального мира будет:

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

С уже довольно стандартным приложением или структурой frameworkd, подобной вышеизложенной, вы часто получаете оптимизированный для него веб-сервер, который переписывает URL-адреса для него (настоящий "URL без SEO" будет: 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

с сочетанием "сформированных путей" аргументов и "сформированных запросов".

2

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

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

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

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

1

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

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

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

0

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню