Найкращі практики REST API: Де розмістити параметри? [зачинено]


348

API REST може мати параметри принаймні двома способами:

  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 та проект sepc були дуже корисними та цікавими! :-)
KajMagnus

1
Тест помилки 404 мені дуже допомагає уникати введення інформації в шлях, який належить до параметрів запиту, заголовків або тіла запиту. Дякуємо за точку!
Кевін Кондон

152

Пізня відповідь, але я додам деяке додаткове розуміння того, що поділилося, а саме, що до запиту є кілька типів "параметрів", і ви повинні це врахувати.

  1. Локатори - наприклад, ідентифікатори ресурсів, такі як ідентифікатори або дія / перегляд
  2. Фільтри - наприклад, параметри, які забезпечують пошук, сортування або звуження набору результатів.
  3. Стан - Наприклад ідентифікація сеансу, клавіші api, whatevs.
  4. Зміст - Напр. Дані, що підлягають збереженню.

Тепер давайте розглянемо різні місця, куди ці параметри могли б перейти.

  1. Попросіть заголовки та файли cookie
  2. Рядок запиту URL-адреси ("GET" vars)
  3. URL-шляхи
  4. Рядок / багаточастинний запит для тіла ("POST" vars)

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

Аналогічно, Вміст має належати лише одному місцю, що знаходиться в тілі запиту, або як рядки запитів, або як багатоповерховий та / або контент JSON. Це відповідає тому, що ви отримуєте від сервера, коли він надсилає вам вміст. Тож ви не повинні бути грубими і робити це по-іншому.

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

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

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

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

Сподіваюсь, це допомогло подарувати людям декілька моментів Юреки, якщо вони загубилися, де розмістити речі!


2
Чому тоді не idфільтр? Він повертає підмножину ресурсу
Джонатан.

13
@Jonathan. ні, він не повертає конкретний ресурс, а саме номер статті 5. Фільтр - це завжди спосіб звузити пошук у колекції ресурсів. Якщо ви хочете саме цього конкретного ресурсу, тоді повинен бути визначений спосіб отримати це. Фільтрування означає, що у вас є можливість повернути кілька ресурсів. Ідентифікатор - це не фільтр, це певний єдиний ресурс. Якщо у вас RANGE ідентифікаторів, то це був би фільтр, навіть якщо діапазон просто включав один ідентифікатор. Якщо фільтр також включав типи ресурсів, він повертав би всі ресурси з ідентифікатором 5, а не лише статтю.
Tor Valamo

1
@Jonathan .: як згадував DarrelMiller, ви очікуєте, що запит на об’єкт / id поверне 404 у випадку невідомого ідентифікатора, тоді як ви очікуєте повернення об'єкта? Id = id та порожнього списку. Також я вважаю, що будь-який тип фільтрації / підмножини повинен повертати список.
njzk2

1
Сторінки важкі, тому що, як ви кажете, це може бути фільтр ресурсу (колекція сторінок), але тоді він є певним ресурсом у цій колекції. Я завжди б просив сторінку статті за допомогою локатора, а не фільтрувати. Однак сторінка може бути фільтром списку чогось, скажімо, списку користувачів. Але тоді сторінка за своєю суттю є розмежувачем, він називається "почати з пункту (page-1)*perpageта показати perpageелементи". Використовувати його як фільтр є правильним тоді, але з різних причин. Називати його "сторінкою" технічно неправильно. Більш семантично правильним було б називати це "від" або "startAt"
Tor Valamo

1
(продовження) Семантичне значення "сторінки" полягає в тому, що це конкретний ресурс, який не змінюється. Він походить від фізичного друку. Якби у нас ніколи не було книг чи друкованих матеріалів, "сторінка" насправді не була б словом. Якщо у вас є динамічний список елементів, розділених на "сторінки", вам слід дійсно вказати конкретну початкову точку, чисельну, алфавітну або навіть окрему, а також фільтр "скільки на сторінку". Якщо я хочу посилатися на щось у вашому списку, я хочу конкретизувати. Також я не хочу переходити на сторінку 5, аби тільки зрозуміти, що ви змінили внутрішнє значення perpageна 50 замість 20.
Tor Valamo

21

Це залежить від конструкції. На REST через HTTP немає правил для URI (головне, щоб вони були унікальними). Часто справа стосується смаку та інтуїції ...

Я використовую наступний підхід:

  • URL-елемент path: ресурс та його елемент path утворює обхід каталогу та підресурс (наприклад, / items / {id}, / users / items). Якщо ви не впевнені, запитайте своїх колег, чи вважають вони це обхід, і вони думають, що в "іншому каталозі" найімовірніший елемент шляху - це правильний вибір
  • параметр url: коли насправді немає обходу (ресурси пошуку з декількома параметрами запиту - дуже приклад для цього)

1
Насправді є досить чіткі правила того, як повинен виглядати URI, і дуже мало двозначності щодо того, як застосувати їх до RESTful URI.
DanMan

18

Параметри IMO повинні бути кращими як аргументи запитів. 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
Darrel Miller

2
@DarrelMiller Дякую! Моє запитання випливає з того, що в основному посередницькі компоненти HTTP не кешують відповіді на запити, що містять рядок запиту. Отже, здається, що параметри запиту більш придатні для пошуку ресурсів за деякими критеріями, а не для однозначної ідентифікації ресурсу.
Мікаель Марраш

17

Відповідно до впровадження REST,

1) Змінні контуру використовуються для прямої дії на ресурси, наприклад, контакт або пісня, наприклад.
GET etc / api / resource / {songid} або
GET etc / api / resource / { contactid} поверне відповідні дані.

2) Пермські запити / аргумент використовуються для безпосередніх ресурсів, таких як метадані пісні ex .., GET / api / resource / {songid}? Metadata = жанри, вони повернуть дані про жанри для певної пісні.


5
Насправді не існує стандарту REST . За Вікіпедією : На відміну від веб-сервісів на основі SOAP, немає "офіційного" стандарту для RESTful веб-API. [14] Це тому, що REST - це архітектурний стиль, на відміну від SOAP, який є протоколом. Навіть незважаючи на те, що REST не є стандартом, реалізація RESTful, така як Інтернет, може використовувати такі стандарти, як HTTP, URI, XML тощо.
DavidRR

Мені не подобається 2 підхід. Я вважаю за краще преффер / api / жанри? Songid = 123 або / api / songs / {song-id} / жанри
Bart Calixto

1
@Bart, Satish мав на увазі Змінні на шляху, що по суті є тим, на що ви посилаєтесь як у своїх уподобаннях ... однак, якщо жанри насправді є метаданими, а не полем композиції / ресурсу пісні .. тоді я міг би побачити більше чутливості у використанні рядка запиту на ньому ..
Brett Caswell

@BrettCaswell отримав це! дякую, що вказали на це. дуже ціную це!
Барт Калікто

16

"Пакуйте" та розмістіть свої дані на "контексті", який забезпечує всесвіт-ресурс-локатор, що означає №1 заради локатора.

Пам'ятайте обмеження №2. Я віддаю перевагу POSTs №1.

Примітка: обмеження обговорюються для

POST в Чи існує максимальний розмір вмісту параметра POST?

GET в Чи існує обмеження на тривалість запиту GET? та Максимальний розмір параметрів URL-адреси в _GET

ps ці обмеження базуються на можливостях клієнта (браузер) та сервері (конфігурація).


Додаток: дотепні маршрути можуть мати версії (розрізняти за заголовками), таким чином забезпечують розвинуту функціональність, не потрібно змінювати код, що споживає код, заповнений рештою (api), який ви пишете як у перезавантаженні -> шукайте
версійні

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 API Twitter), параметри являють собою перехідний запит, який фільтрує стан ресурсів (а не ієрархію). У цьому конкретному прикладі було б цілком необґрунтовано додавати до колекції, представленої цими обмеженнями, і далі, що запит не зможе бути представлений як шлях, який би мав сенс у термінах об'єктного графіка.

Прийняття цього виду орієнтованого на ресурси перспективи може легко відображати безпосередньо на об'єктному графіку вашої доменної моделі та керувати логікою вашого API до того моменту, коли все працює дуже чисто і досить документуючи, як тільки він чітко розкриється. Концепцію можна також зробити більш зрозумілою, відступившись від систем, які використовують традиційну маршрутизацію URL-адрес, відображену на звичайно непридатній моделі даних (тобто RDBMS). 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 = значення).

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

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

Незалежно від того, який ви вибрали, переконайтесь, що ви вибрали лише один, не змішуйте та не співпадайте. Це призводить до заплутаного API.


2

Один «вимір» цієї теми був опущений, але це дуже важливо: бувають випадки, коли «найкращі практики» повинні змиритися з платформою, яку ми впроваджуємо або доповнюємо з можливостями REST.

Практичний приклад:

У наш час багато веб-додатків реалізують архітектуру MVC (Model, View, Controller). Вони припускають, що певний стандартний шлях передбачений, тим більше, коли ці веб-додатки мають опцію "Увімкнути 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

Завдяки досить стандартному застосуванню або структурі framed, подібній до вище, ви часто отримуєте оптимізований для нього веб-сервер, який переписує URL-адреси для нього (справжній "не SEOed URL" буде:) http://www.domain.tld/index.php?route=special-offers/list-all&limit=25.

Тому ви, як розробник, стикаєтеся з існуючою інфраструктурою та адаптуєте свої "найкращі практики", якщо ви не системний адміністратор, точно знаєте, як налаштувати конфігурацію переписування Apache / NGinx (остання може бути неприємною!) І так на.

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

Щоб повернутися до практичного прикладу вище, послідовним API REST буде щось із URL-адресами на зразок:

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

або (не SEO-адреси)

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

із поєднанням аргументів "сформовані шляхи" та "сформовані запити".


1

Я бачу багато API REST, які не добре обробляють параметри. Одним із прикладів, які часто з’являються, є те, коли URI включає в себе особисту інформацію.

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

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


0

Це дуже цікаве питання.

Ви можете використовувати обидва, не існує жодного суворого правила щодо цієї теми, але використання змінних шляху URI має деякі переваги:

  • Кеш : Більшість сервісів веб-кеша в Інтернеті не кешують GET-запит, коли вони містять параметри запиту. Вони роблять це тому, що існує багато RPC-систем, які використовують 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.