Виклик серверного методу на ресурсі RESTful способом


142

Майте на увазі, що я розумію REST. Скажімо, у мене є така URL-адреса:

http://api.animals.com/v1/dogs/1/

А тепер я хочу змусити сервер зробити собачий гавкіт. Тільки сервер знає, як це зробити. Скажімо, я хочу, щоб він працював на роботі CRON, що змушує собаку брахати кожні 10 хвилин протягом усієї вічності. Як виглядає цей дзвінок? Я наче хочу зробити це:

Запит URL-адреси:

ACTION http://api.animals.com/v1/dogs/1/

В органі запиту:

{"action":"bark"}

Перш ніж ви зліться на мене за створення мого власного методу HTTP, допоможіть мені і дайте мені краще уявлення про те, як я повинен викликати метод на стороні сервера RESTful способом. :)

РЕДАКТУВАННЯ ДЛЯ В’ЯЗКУ

Ще кілька роз’яснень щодо того, що робить метод «кору». Ось кілька варіантів, які можуть спричинити за собою різні структуровані дзвінки API:

  1. кора просто надсилає електронний лист на dog.email і нічого не записує.
  2. кора відправляє електронний лист на dog.email, а надбавки dog.barkCount на 1.
  3. Кора створює нову "кору" запис із записом bark.timestamp, коли кора виникає. Він також збільшує dog.barkCount на 1.
  4. bark запускає системну команду, щоб витягнути останню версію собачого коду вниз від Github. Потім він надсилає текстове повідомлення власнику dog.owner, вказуючи їм, що новий код собаки знаходиться у виробництві.

14
Цікаво, що додавання щедрості, як видається, привернуло гірші відповіді, ніж ви були спочатку ;-) При оцінці відповідей пам’ятайте, що: 1) Технічні характеристики для дієслів HTTP виключають будь-який вибір, крім POST. 2) REST не має нічого спільного з структурою URL - це загальний перелік контрагентів (без стану, кешованого, шаруватого, рівномірного інтерфейсу тощо), ніж надає переваги (масштабованість, надійність, видимість тощо). 3) Сучасна практика (наприклад, використання POST у специфікаціях RPC) козирує дефініалістами, які складають свої власні правила API. 4) REST вимагає рівномірного інтерфейсу (дотримуючись специфікації HTTP).
Реймонд Хеттінгер

@Kirk, що ти думаєш про нові відповіді? Чи є ще щось, що ви хочете знати, але не було адресовано в жодному з них? Я б більш ніж радий відредагувати свою відповідь ще раз, якщо вона може бути кориснішою.
Йордан

@RaymondHettinger PATCHможе бути доречним. Я пояснюю, чому наприкінці своєї відповіді .
Йордан

PATCH підходить тільки для збільшення dog.barkCount на один. POST - метод надсилання електронної пошти, створення нової записи кори, виконання команд для завантаження з Github або запуску текстового повідомлення. @ Джордане, ваше читання PATCH RFC є образним, але дещо суперечить його намірам як варіанту PUT для часткової модифікації ресурсів. Я не думаю, що ви допомагаєте ОП, придумуючи нетрадиційне зчитування специфікацій HTTP, а не визнаючи стандартну практику використання POST для віддалених процедурних дзвінків.
Реймонд Хеттінгер

@RaymondHettinger, чия практика фактично стандартизує POST? Усі стандартні інтерфейси RPC, які я бачив, ідентифікують ресурс за сутністю (не RESTful), а не URI, тому дійсна відповідь, що визначає пріоритетність конвенції RPC, все одно повинна бути нетрадиційною, що, на мою думку, спростовує значення звичайного RPC: один є уявним або непослідовним . Ви ніколи не можете помилитися з POST, оскільки це загальна обробка даних, але є більш конкретні методи. REST означає називати ресурси та описувати зміни у їхньому стані, а не називати процедури, що змінюють стан. PATCH і POST описують зміни стану.
Йордан

Відповіді:


280

Навіщо прагнути до проекту RESTful?

Принципи RESTful приводять функції, які спрощують веб-сайти (для випадкового користувача, щоб " користуватися ними") до дизайну API веб-служб , тому програмісту вони легко користуються. REST - це не добре, оскільки це REST, це добре, тому що це добре. І це здебільшого добре тому, що це просто .

Простота звичайного HTTP (без конвертів SOAP та перевантажених POSTслужб одноразового URI ), що деякі можуть назвати «відсутністю функцій» , насправді є його найбільшою силою . Безпосередньо HTTP просить вас мати адресність та без громадянства : два основні дизайнерські рішення, завдяки яким HTTP можна масштабувати до сьогоднішніх мега-сайтів (і мегапослуг).

Але REST - це не срібний буллет: Іноді RPC-стиль ("Віддалений виклик процедури" - наприклад SOAP) може бути доречним , а іноді інші потреби мають перевагу над чеснотами Мережі. Це добре. Те, що нам не дуже подобається, - це непотрібність . Занадто часто програміст або компанія надає послуги стилю RPC для роботи, з якою звичайний старий HTTP міг би справитись чудово. Ефект полягає в тому, що HTTP зводиться до транспортного протоколу для величезної корисної навантаження XML, яка пояснює, що відбувається "насправді" (не URI або метод HTTP не дають поняття про це). Отримана послуга є надто складною, її неможливо налагодити, і вона не працюватиме, якщо ваші клієнти не встановлять точну настройку, як планував розробник.

Само # код Java / C може бути НЕ об'єктно-орієнтованим, тільки з допомогою HTTP не робить дизайн RESTful. Хтось може потрапити в поспіх думати про свої послуги з точки зору дій та віддалених методів, які слід викликати. Недарма це в основному закінчиться послугою RPC-Style (або гібридом REST-RPC). Перший крок - думати інакше. RESTful дизайн може бути досягнутий багатьма способами. Один із способів - це продумати свою програму з точки зору ресурсів, а не з дій:

💡 Замість того, щоб думати з точки зору дій, які вона може виконувати ("зробіть пошук місць на карті") ...

... спробуйте продумати результати цих дій ("список місць на карті, що відповідають критеріям пошуку").

Я піду для прикладів нижче. (Інший ключовий аспект REST - це використання HATEOAS - я не наношу його тут, але швидко говорю про це на іншій посаді .)


Випуски першої конструкції

Давайте розглянемо запропонований дизайн:

ACTION http://api.animals.com/v1/dogs/1/

По-перше, ми не повинні розглянути можливість створення нового HTTP verb ( ACTION). Взагалі, це небажано з кількох причин:

  • (1) Враховуючи лише URI служби, як "випадковий" програміст дізнається, чи ACTIONіснує дієслово?
  • (2) якщо програміст знає, що він існує, то як він буде знати його семантику? Що означає це дієслово?
  • (3) які властивості (безпека, ідентифікація) слід очікувати від цього дієслова?
  • (4) що робити, якщо програміст має дуже простий клієнт, який обробляє лише стандартні HTTP-дієслова?
  • (5) ...

Тепер давайте розглянемо використанняPOST (я обговорю, чому нижче, просто візьміть моє слово зараз):

POST /v1/dogs/1/ HTTP/1.1
Host: api.animals.com

{"action":"bark"}

Це може бути нормально ... але тільки якщо :

  • {"action":"bark"}був документ; і
  • /v1/dogs/1/був URI "процесор документування" (подібний до заводських). "Процесор документа" - це URI, який ви просто "кидаєте на речі" і "забудете" про них - процесор може перенаправити вас на новостворений ресурс після "кидання". Наприклад, URI для розміщення повідомлень у службі посередника повідомлень, яка після публікації перенаправить вас на URI, який показує стан обробки повідомлення.

Я мало знаю про вашу систему, але я вже ставлюся на те, що обидва не відповідають дійсності:

  • {"action":"bark"} це не документ , це фактично метод, який ви намагаєтеся ніндзя прокрастися до служби; і
  • /v1/dogs/1/URI являє собою «собака» ресурс (ймовірно, собака з id==1) , а не документ процесора.

Отже, все, що ми знаємо зараз, - це те, що дизайн вище не такий ВИПАДНИЙ, але що це саме? Що в цьому так поганого? В основному це погано, оскільки це складний URI зі складними значеннями. Ви нічого не можете зробити з цього. Як програміст знає, що собака має barkдії, які можна таємно влити POSTв неї?


Розробка API-викликів вашого запитання

Тож давайте зупинимось на погоні і спробуємо спроектувати ці гайки, подумавши з точки зору ресурсів . Дозвольте мені цитувати книгу "Відпочиваючі веб-служби" :

POSTЗапит є спробою створити новий ресурс з існуючого. Існуючий ресурс може бути батьківським новим у сенсі структури даних, так як корінь дерева є батьківським для всіх його листкових вузлів. Або існуючий ресурс може бути спеціальним "заводським" ресурсом, єдиною метою якого є генерування інших ресурсів. Представлення, надіслане разом із POSTзапитом, описує початковий стан нового ресурсу. Як і у випадку PUT, POSTзапит взагалі не повинен містити представлення.

Після опису вище , ми можемо побачити , що barkможе бути змодельовані як в subresource зdog (так як barkміститься всередині собаки, тобто кора «вкорінення» від собаки).

З цього міркування ми вже отримали:

  • Метод є POST
  • Ресурс є /barks, субресурс собаки:, що /v1/dogs/1/barksпредставляє bark"фабрику". Цей URI унікальний для кожної собаки (оскільки вона знаходиться під /v1/dogs/{id}).

Тепер кожен випадок вашого списку має певну поведінку.

1. Кора просто надсилає електронний лист dog.emailі нічого не записує.

По-перше, чи гавкання (надсилання електронного листа) є синхронним чи асинхронним завданням? По-друге, чи barkвимагає запит будь-який документ (можливо, електронний лист) чи він порожній?


1.1 кора відправляє електронний лист dog.emailі нічого не записує (як синхронне завдання)

Цей випадок простий. Виклик на barksзаводський ресурс дає кору (електронний лист надіслано) одразу, і відповідь (якщо гаразд чи ні) надається відразу:

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

(entity-body is empty - or, if you require a **document**, place it here)

200 OK

Оскільки він нічого не записує (не змінює), 200 OKцього достатньо. Це показує, що все пройшло так, як очікувалося.


1.2 кора відправляє електронний лист dog.emailі нічого не записує (як асинхронне завдання)

У цьому випадку клієнт повинен мати спосіб відстежити barkзавдання. Тоді barkзавдання повинно бути ресурсом із власним URI:

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

{document body, if needed;
NOTE: when possible, the response SHOULD contain a short hypertext note with a hyperlink
to the newly created resource (bark) URI, the same returned in the Location header
(also notice that, for the 202 status code, the Location header meaning is not
standardized, thus the importance of a hipertext/hyperlink response)}

202 Accepted
Location: http://api.animals.com/v1/dogs/1/barks/a65h44

Таким чином, кожен barkпростежується. Потім клієнт може GETнадати barkURI, щоб знати, що це поточний стан. Можливо, навіть скористайтеся а, DELETEщоб скасувати це.


2. Кора надсилає електронну пошту до, dog.emailа потім з кроком dog.barkCountна 1

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

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

{document body, if needed; when possible, containing a hipertext/hyperlink with the address
in the Location header -- says the standard}

303 See Other
Location: http://api.animals.com/v1/dogs/1

У цьому випадку locationзаголовок має намір повідомити клієнту, що він повинен поглянути dog. З HTTP RFC про303 :

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

Якщо завдання асинхронне, потрібний barkсубресурс, як і 1.2ситуація, і він 303повинен бути повернутий у GET .../barks/Yмомент, коли завдання завершено.


3. Кора створює нову " bark" запис із bark.timestampзаписом, коли відбулася кора. Він також збільшується dog.barkCountна 1.

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

(document body, if needed)

201 Created
Location: http://api.animals.com/v1/dogs/1/barks/a65h44

Тут barkстворено завдяки запиту, тому застосовується статус 201 Created.

Якщо створення асинхронне, натомість 202 Acceptedпотрібно ( як говорить HTTP RFC ).

Збережена часова марка є частиною barkресурсу і її можна отримати за допомогою a GET. Оновлену собаку також можна «задокументувати» GET dogs/X/barks/Y.


4. Брай виконує системну команду, щоб витягнути останню версію собачого коду вниз від Github. Потім він надсилає текстове повідомлення, щоб dog.ownerповідомити їм, що новий код собаки знаходиться у виробництві.

Формулювання цього є складним, але це, як правило, є простим асинхронним завданням:

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

(document body, if needed)

202 Accepted
Location: http://api.animals.com/v1/dogs/1/barks/a65h44

Потім клієнт видасть GETs, щоб /v1/dogs/1/barks/a65h44знати поточний стан (якщо код був витягнутий, його електронна пошта була надіслана власнику тощо). Щоразу, коли собака змінюється, а 303можна піддати.


Підведенню

Цитуючи Роя Філдінга :

Єдине, що REST вимагає від методів, це те, щоб вони були рівномірно визначені для всіх ресурсів (тобто, щоб посередники не знали тип ресурсу, щоб зрозуміти значення запиту).

У наведених вище прикладах POSTрозроблено рівномірно. Це зробить собаку " bark". Це не безпечно (означає, що кора впливає на ресурси), ні idempotent (кожен запит дає новий bark), що добре відповідає POSTдієслову.

Програміст знає: а POSTдо barksприбутків а bark. Коди статусу відповіді (також, якщо це необхідно, із суттю-органом та заголовками) виконують завдання пояснення того, що змінилося, і як клієнт може і повинен діяти.

Примітка. Основними використаними джерелами були: книга " Відпочиваючі веб-сервіси ", HTTP RFC та блог Роя Філдінга .




Редагувати:

Запитання, а отже, і відповідь досить сильно змінилися з моменту їх створення. Оригінальний питання поставлено питання про дизайн URI , як:

ACTION http://api.animals.com/v1/dogs/1/?action=bark

Нижче наведено пояснення, чому це не вдалий вибір:

Як клієнти говорять серверу, ЩО РОБИТИ з даними - це інформація про метод .

  • RESTful веб-сервіси передають інформацію про метод у методі HTTP.
  • Типові сервіси RPC-стилю та SOAP зберігають своє місце в тілі об'єкту та HTTP-заголовку.

НА ЯКІЙ ЧАСТИНИ даних (клієнт хоче, щоб сервер) працював, це інформація про скопіювання .

  • RESTful сервіси використовують URI. Сервіси SOAP / RPC-стилю вкотре використовують заголовки тіла та тіла та HTTP.

Як приклад, візьміть URI Google http://www.google.com/search?q=DOG. Там інформація про метод є, GETа інформація про масштаби є /search?q=DOG.

Довга коротка історія:

  • У архітектурах RESTful інформація про метод переходить у метод HTTP.
  • У архітектурах , орієнтованих на ресурси , інформація про масштаби передається в URI.

І правило:

Якщо метод HTTP не відповідає інформації про метод, служба не RESTful. Якщо інформація про розміщення відсутня в URI, сервіс не орієнтований на ресурси.

Ви можете помістити "кора" "дії" в URL-адресу (або в тіло-сутність) і використовувати POST. Тут немає жодних проблем, це працює, і, можливо, це найпростіший спосіб зробити це, але це НЕ РЕЗУЛЬТАТИ .

Щоб ваша послуга була дійсно ВІДБУДОВОЮ, можливо, вам доведеться зробити крок назад і подумати про те, що ви насправді хочете зробити тут (які наслідки це матиме на ресурсах).

Я не можу говорити про ваші конкретні бізнес-потреби, але дозвольте навести приклад. Розгляньте послугу RESTful замовлення, де замовлення виконуються у таких URI example.com/order/123.

Тепер скажіть, що ми хочемо скасувати замовлення, як це робити? Можна спокуситись подумати, що це "скасування" "дії", і спроектувати це якPOST example.com/order/123?do=cancel .

Це не ВИПУСК, як ми говорили вище. Натомість ми можемо PUTнове представлення елемента orderз canceledелементом, надісланим true:

PUT /order/123 HTTP/1.1
Content-Type: application/xml

<order id="123">
    <customer id="89987">...</customer>
    <canceled>true</canceled>
    ...
</order>

І це все. Якщо замовлення неможливо скасувати, певний код статусу можна повернути. (Дизайн субресурсів, як POST /order/123/canceledі суб'єкт-орган true, для простоти також може бути доступним.)

У вашому конкретному сценарії ви можете спробувати щось подібне. Таким чином, в той час як собака гавкає, наприклад, GETв /v1/dogs/1/можете включити цю інформацію (наприклад <barking>true</barking>) . Або ... якщо це занадто складно, послабте свою ВІДПОВІДНУ вимогу і дотримуйтесь POST.

Оновлення:

Я не хочу робити відповідь занадто великим, але потрібен певний час, щоб отримати змогу викрити алгоритм ( дію ) як набір ресурсів. Замість того, щоб думати з точки зору дій ( "виконайте пошук місць на карті" ), потрібно думати з точки зору результатів цієї дії ( "список місць на карті, що відповідають критеріям пошуку" ).

Ви можете повернутися до цього кроку, якщо виявите, що ваш дизайн не відповідає єдиному інтерфейсу HTTP.

Змінні запиту є інформацією про масштаби , але не позначають нові ресурси ( /post?lang=enце явно той самий ресурс, як /post?lang=jpпросто інше представлення). Швидше, вони використовуються для передачі стану клієнта (наприклад ?page=10, щоб цей стан не зберігався на сервері; ?lang=enтут також є прикладом) або вхідних параметрів до алгоритмічних ресурсів ( /search?q=dogs, /dogs?code=1). Знову ж таки, не окремі ресурси.

Властивості (методи) дієслів HTTP:

Ще один чіткий момент, який відображається ?action=somethingв URI, не є RESTful, - це властивості дієслів HTTP:

  • GETі HEADє безпечними (і ідентичними);
  • PUTі DELETEє лише ідентичними;
  • POST це не те.

Безпека : GETабо HEADзапит - це запит на читання деяких даних, а не запит на зміну будь-якого стану сервера. Клієнт може зробити GETабо HEADзапит 10 разів, і це те саме, що зробити його один раз, або взагалі ніколи не робити його .

Ідентифікація : Ідентифікаційна операція в тій, яка має той самий ефект, незалежно від того, застосовуєте ви її один раз або більше одного разу (в математиці множення на нуль - ідентичне). Якщо ви DELETEодин раз використовуєте ресурс, його видалення матиме такий же ефект (ресурс GONEвже є ).

POSTне є ні безпечним, ні безсильним. Надання двох однакових POSTзапитів на "заводський" ресурс, ймовірно, призведе до двох підпорядкованих ресурсів, що містять однакову інформацію. При перевантаженні (метод в URI або суті) POSTвсе ставки відміняються.

Обидва ці властивості були важливими для успіху протоколу HTTP (у ненадійних мережах!): Скільки разів ви оновлювали ( GET) сторінку, не чекаючи її повного завантаження?

Створення дії та розміщення її в URL-адресі явно порушує контракт методів HTTP. Знову ж таки, технологія дозволяє, ви можете це зробити, але це не RESTful дизайн.


Я погоджуюся з думкою, що виклик дії на сервері, позначеного як дія в URL, не є RESTful. POSTбув розроблений для "надання блоку даних ... до процесу обробки даних" . Здається, багато людей відрізняють ресурси від дій, але насправді дії - це лише тип ресурсу.
Джейкоб Стівенс

1
@JacobStevens ОП трохи змінив питання, тому мені доведеться оновити свою відповідь, щоб зробити його більш прямим (перевірте оригінальне запитання , можливо, ви побачите, що я маю на увазі). Я погоджуюся з POST"наданням блоку даних ... до процесу обробки даних", але різниця полягає в тому, що блок даних , а не блок даних, і процедура (дія, метод, команда) страчений тоді. Це POSTперевантаження, а POSTперевантаження - це дизайн у стилі RPC, а не RESTful.
acdcjunior

Я припускаю, що логіка дії / методу буде розміщена на сервері, інакше, якою була б мета виклику? У випадку, який ви описуєте, я згоден, це не був би гарний дизайн. Але метод або підпрограма, яка виконує дію, буде визначена URI (це ще одна причина, чому ресурс дії, призначений як дієслово в кінці URL-адреси, є корисним і RESTful, хоча багато хто радить проти цього).
Джейкоб Стівенс

6
Відповідь нам оновлена. Трохи довго, тому що здавалося необхідним ґрунтовне пояснення ("Майте на увазі, я розумію REST".). Це була своєрідна боротьба, щоб зробити її максимально зрозумілою. Сподіваюся, це корисно в чомусь.
acdcjunior

2
Чудове пояснення, я проголосував, але заголовок Location не повинен використовуватися у відповіді 202 Прийнято. Здається, це неправильне тлумачення, яке багато людей роблять з RFC. Перевірте stackoverflow.com/questions/26199228 / ...
Delmo

6

Я відповів раніше , але ця відповідь суперечить моїй старій відповіді і слідує за зовсім іншою стратегією для досягнення рішення. Він показує, як HTTP-запит будується з концепцій, які визначають REST та HTTP. Він також використовує PATCHзамість POSTабоPUT .

Він проходить через обмеження REST, потім компоненти HTTP, потім можливе рішення.

ВІДПОВІДНИЙ

REST - це набір обмежень, призначених для застосування до розподіленої системи гіпермедіа, щоб зробити її масштабованою. Навіть щоб мати сенс цього в контексті віддаленого контролю над дією, ви повинні думати про віддалене керування дією як частиною розподіленої системи гіпермедіа - частиною системи виявлення, перегляду та зміни взаємопов'язаної інформації. Якщо це більше клопоту, ніж це варто, то, мабуть, не годиться намагатися зробити його ВІДГОТОВОМ. Якщо ви просто хочете на клієнті GUI типу "панель управління", який може викликати дії на сервері через порт 80, ви, ймовірно, хочете простий інтерфейс RPC, наприклад JSON-RPC, через HTTP запити / відповіді або WebSocket.

Але REST - це захоплюючий спосіб мислення, і приклад у питанні, здається, легко моделювати з RESTful інтерфейсом, тому давайте взяти на себе завдання для розваги та для навчання.

REST визначається чотирма обмеженнями інтерфейсу:

ідентифікація ресурсів; маніпулювання ресурсами через представництва; повідомлення з самоописанням; і, гіпермедіа як двигун стану застосування.

Ви запитуєте, як можна визначити інтерфейс, відповідаючи цим обмеженням, за допомогою яких один комп’ютер повідомляє іншому комп'ютеру зробити собачий гавкіт. Зокрема, ви хочете, щоб ваш інтерфейс був HTTP, і ви не хочете відмовлятися від функцій, які роблять HTTP RESTful при використанні за призначенням.

Почнемо з першого обмеження: ідентифікація ресурсів .

Будь-яка інформація, яку можна назвати, може бути ресурсом: документ або зображення, тимчасова послуга (наприклад, "сьогоднішня погода в Лос-Анджелесі"), колекція інших ресурсів, невіртуальний об'єкт (наприклад, людина) тощо .

Тож собака - ресурс. Це потрібно визначити.

Точніше, ресурс R - це часово мінлива функція членства M R ( t ), яка за час t відображає набір сутностей або значень, які є еквівалентними. Значення в наборі можуть бути представленнями ресурсів та / або ідентифікаторами ресурсів .

Ви моделюєте собаку, беручи набір ідентифікаторів та уявлень і говорячи, що всі вони пов'язані між собою в даний момент часу. Поки ж давайте скористаємося ідентифікатором "собака №1". Це приводить нас до другого і третього обмежень: представлення ресурсів та самоопис .

Компоненти REST виконують дії на ресурсі, використовуючи подання для фіксації поточного або передбачуваного стану цього ресурсу та передачі цього представлення між компонентами. Представлення - це послідовність байтів, плюс метадані представлення для опису цих байтів.

Далі йде послідовність байтів, що фіксують призначений стан собаки, тобто представлення, яке ми хочемо пов'язати з ідентифікатором "собака №1" (зауважте, що воно є лише частиною стану, оскільки воно не враховує ім'я собаки, стан здоров'я або навіть минулі лайки):

Він гавкає кожні 10 хвилин, з моменту здійснення цієї зміни, і триватиме нескінченно.

Він повинен бути доданий до метаданих, які описують його. Ці метадані можуть бути корисними:

Це англійська заява. Він описує частину запланованого стану. Якщо він отриманий кілька разів, дозвольте першим мати ефект.

Нарешті, давайте розглянемо четверте обмеження: HATEOAS .

REST ... розглядає додаток як згуртовану структуру інформації та альтернативи управління, за допомогою яких користувач може виконати бажане завдання. Наприклад, пошук слова в он-лайн словнику - це одна програма, як і гастролі по віртуальному музею, або перегляд набору конспектів класів для вивчення іспиту. ... Наступний контрольний стан програми знаходиться у поданні першого запитуваного ресурсу, тому отримання цього першого представлення є пріоритетним. ... Таким чином, модельна програма - це двигун, який переходить від одного стану до іншого, досліджуючи та вибираючи серед альтернативних переходів стану в поточному наборі представлень.

У інтерфейсі RESTful клієнт отримує представлення ресурсів для того, щоб зрозуміти, як він повинен приймати або відправляти представлення. Десь у програмі повинно бути представлення, з якого клієнт може зрозуміти, як приймати або надсилати всі представлення, які він повинен мати можливість приймати або надсилати, навіть якщо воно слід за ланцюгом представлень, щоб дістатись до цієї інформації. Це здається досить простим:

Клієнт просить представити ресурс, ідентифікований як домашня сторінка; у відповідь він отримує представлення, яке містить ідентифікатор кожної собаки, яку може захотіти клієнт. Клієнт дістає з нього ідентифікатор і запитує службу, як вона може взаємодіяти з ідентифікованою собакою, а служба каже, що клієнт може надіслати англійську заяву, що описує частину передбачуваного стану собаки. Потім клієнт надсилає таку заяву і отримує повідомлення про успіх або повідомлення про помилку.

HTTP

HTTP реалізує обмеження REST таким чином:

ідентифікація ресурсу : URI

представлення ресурсів : суб'єкт-орган

самоопис : метод або код стану, заголовки та, можливо, частини тіла-об'єкта (наприклад, URI схеми XML)

HATEOAS : гіперпосилання

Ви вирішили http://api.animals.com/v1/dogs/1 як URI. Припустимо, клієнт отримав це з якоїсь сторінки на сайті.

Давайте скористаємося цим органом-сутністю (значення next- це часова марка; значення 0означає, "коли цей запит отримано"):

{"barks": {"next": 0, "frequency": 10}}

Тепер нам потрібен метод. PATCH відповідає опису "частини запланованого стану", про який ми вирішили:

Метод PATCH просить застосувати набір змін, описаних в об'єкті запиту, до ресурсу, визначеного Request-URI.

І деякі заголовки:

Щоб вказати мову суб'єкта-органу: Content-Type: application/json

Щоб переконатися, що це відбувається лише один раз: If-Unmodified-Since: <date/time this was first sent>

І у нас є запит:

PATCH /v1/dogs/1/ HTTP/1.1
Host: api.animals.com
Content-Type: application/json
If-Unmodified-Since: <date/time this was first sent>
[other headers]

{"barks": {"next": 0, "frequency": 10}}

Після успіху клієнт повинен отримати 204код статусу у відповідь, або 205якщо представлення /v1/dogs/1/змінилося, щоб відобразити новий графік гавкання.

У разі відмови він повинен отримати а 403 корисне повідомлення, чому.

REST не важливо, щоб служба відображала графік кори в поданні у відповідь GET /v1/dogs/1/, але було б найбільш сенс, якби представлення JSON включало це:

"barks": {
    "previous": [x_1, x_2, ..., x_n],
    "next": x_n,
    "frequency": 10
}

Розглядайте завдання cron як деталі реалізації, які сервер ховає від інтерфейсу. Ось у чому краса загального інтерфейсу. Клієнт не повинен знати, що сервер робить за кадром; все, що її хвилює, - це те, що служба розуміє та реагує на запитувані зміни стану.


3

Більшість людей використовують POST для цієї мети. Він підходить для виконання "будь-якої небезпечної або недемонтованої операції, коли жоден інший метод HTTP не здається доцільним".

Такі API, як XMLRPC, використовують POST для запуску дій, які можуть виконувати довільний код. "Дія" включена в дані POST:

POST /RPC2 HTTP/1.0
User-Agent: Frontier/5.1.2 (WinNT)
Host: betty.userland.com
Content-Type: text/xml
Content-length: 181

<?xml version="1.0"?>
<methodCall>
   <methodName>examples.getStateName</methodName>
   <params>
      <param>
         <value><i4>41</i4></value>
         </param>
      </params>
   </methodCall>

Приклад RPC наведено, щоб показати, що POST - це звичайний вибір дієслів HTTP для методів на стороні сервера. Ось думки Роя Філдінга про POST - він, напевно, каже, що ВИКОРИСТНО використовувати методи HTTP як зазначено.

Зауважте, що RPC сам по собі не дуже RESTful, оскільки не орієнтований на ресурси. Але якщо вам потрібні без громадянства, кешування чи розшарування, не важко зробити відповідні перетворення. Для прикладу див. Http://blog.perfectapi.com/2012/opinionated-rpc-apis-vs-restful-apis/ .


Я думаю, ви б URL-код коду, не ставте його в рядок запиту
tacos_tacos_tacos

@Kirk Так, але з однією незначною модифікацією відкиньте остаточну нахилу вперед: POST api.animals.com/v1/dogs1?action=bark
Реймонд Хеттінгер

якщо ви будете дотримуватися порад у цій відповіді, майте на увазі, що отриманий API не буде RESTful.
Ніколас Шенкс

2
Це не RESTful, оскільки HTTP встановлює URL як ідентифікатор ресурсу, а URL /RPC2не робить нічого для ідентифікації ресурсу - він ідентифікує серверну технологію. Натомість це використовується, methodNameщоб спробувати "визначити" "ресурс", але навіть тоді це не виграє від іменника / дієслова; єдина річ, схожа на дієслова methodCall. Це схоже на "робити стан-ім'я-пошук" замість "отримати державну назву" - остання має набагато більше сенсу.
Йордан

+1 для посилань; дуже інформативний, і експеримент "утверджений RPC" є винахідливим.
Йордан

2

POST- метод HTTP, призначений для

Надання блоку даних ... до процесу обробки даних

Серверні методи, що обробляють дії, не відображені на CRUD, - це те, що Рой Філдінг задумав разом з REST, тож ви там добре, і саме тому POSTвін став неідентичним. POSTбуде обробляти більшість публікацій даних на серверних методах для обробки інформації.

Це було сказано, що у вашому сценарії лаяння собак, якщо ви хочете, щоб кора на стороні сервера виконувалась кожні 10 хвилин, але чомусь потрібен тригер, щоб походити від клієнта, PUTбуде слугувати цілі краще через його безсилля. Ну, строго за цим сценарієм немає очевидного ризику, коли багато запитів POST можуть змусити вашу собаку замінити, але все одно, це мета двох подібних методів. Моя відповідь на подібне запитання ТА може бути корисною для вас.


1
PUT vs. POST - це все про URL-адресу. У третьому абзаці після 9.6 PUT зазначено, що мета двох методів полягає в тому, що PUTURL-адреса посилається на те, що слід замінити вмістом клієнта, а POSTURL-адреса посилається на те, що слід обробляти вміст клієнта, як би він не хотів.
Йордан

1

Якщо ми вважаємо, що гавкання - це внутрішній / залежний / допоміжний ресурс, на який може діяти споживач, то можна сказати:

POST http://api.animals.com/v1/dogs/1/bark

собака №1 гавкає

GET http://api.animals.com/v1/dogs/1/bark

повертає останню часову позначку кори

DELETE http://api.animals.com/v1/dogs/1/bark

не застосовується! тому ігноруйте це.


Це тільки RESTful , якщо ви вважаєте /v1/dogs/1/bark, що ресурс сам по собі , і POSTдо опису того , як внутрішній стан цього ресурсу повинно змінитися. Я вважаю, що має більше сенсу просто розглядати /v1/dogs/1/як ресурс і вказувати в суб'єкті-суді, що він повинен брати лай.
Йордан

ммм .. ну, це ресурс, на якому ви можете змінити його стан. Оскільки результат зміни стану - шум, не робить його менш ресурсом! Ви дивитесь на Барку як на дієслово (саме так), тому не можете вважати його ресурсом. Я розглядаю це як залежний ресурс, який може змінити його стан, і оскільки його стан булевий, я не бачу жодної причини згадувати його в сутнісному органі. Це лише моя думка.
болбол

1

Попередні зміни деяких відповідей пропонували використовувати RPC. Вам не потрібно дивитися на RPC , оскільки це є цілком можливо робити те , що ви хочете , в той час як дотримуючись обмежень REST.

По-перше, не вводьте параметри дії в URL. URL-адреса визначає чого ви застосовуєте дію, а параметри запиту є частиною URL-адреси. Це слід мислити цілком як іменник. http://api.animals.com/v1/dogs/1/?action=barkє інший ресурс - інший іменник - to http://api.animals.com/v1/dogs/1/. [nb Asker видалив ?action=barkURI з питання.] Наприклад, порівняйте http://api.animals.com/v1/dogs/?id=1з http://api.animals.com/v1/dogs/?id=2. Різні ресурси, що відрізняються лише рядком запиту. Отже, дія вашого запиту, якщо вона не відповідає безпосередньо безтілесному існуючому типу методу (TRACE, OPTIONS, HEAD, GET, DELETE тощо), повинна бути визначена в органі запиту.

Далі вирішіть, чи є дія " ідентичною ", це означає, що її можна повторити без негативного ефекту (див. Наступний параграф для більшого пояснення). Наприклад, встановлення значення true може бути повторене, якщо клієнт не впевнений, що потрібний ефект стався. Вони надсилають запит ще раз, і значення залишається істинним. Додавання 1 до числа не є ідентичним. Якщо клієнт відправляє команду Add1, не впевнений, що вона працювала, і надсилає її знову, чи додав сервер одну чи дві? Визначивши це, ви можете вибирати метод PUTі POSTспосіб , який ви хочете .

Ідентичний потенціал означає, що запит може бути повторений без зміни результату. Ці ефекти не включають реєстрацію та інші подібні дії адміністратора сервера. Використовуючи ваш перший та другий приклади, надсилання двох електронних листів одній і тій же особі призводить до іншого стану, ніж надсилання одного електронного листа (одержувач має два у своєму вхідному папці, які вони можуть вважати спамом), тому я обов'язково використовую POST для цього . Якщо barkCount у прикладі 2 призначений для перегляду користувачем вашого API або впливає на щось, що бачиться клієнтом, то це також зробить запит не ідентичним. Якщо його слід переглядати лише ви, то він вважається реєстрацією на сервері і повинен ігноруватися при визначенні idempotentcy.

Нарешті, визначте, чи можна очікувати, що дія, яку ви хочете виконати, негайно завершиться чи ні. BarkDog - це швидко закінчується дія. RunMarathon - ні. Якщо ваша дія повільна, розгляньте, чи повернеться а 202 Accepted, з URL-адресою в тілі відповідей, щоб користувач опитував, щоб перевірити, чи завершено дію. Крім того, потрібно, щоб користувачі розмістили POST на такій URL-адресі списку, /marathons-in-progress/а потім, коли дія буде виконано, перенаправити їх з поточної URL-адреси ідентифікатора на/marathons-complete/ URL-адресу.
У конкретних випадках №1 та №2 я б розмістив сервер у чергу, а клієнт розмістив партії адрес до нього. Дія не буде SendEmails, а щось на зразок AddToDispatchQueue. Потім сервер може запитувати чергу, щоб побачити, чи очікуються які-небудь адреси електронної пошти, та надіслати електронні листи, якщо такі знайдеться. Потім він оновлює чергу, щоб вказати, що дія, що очікує, вже виконана. У вас був би інший URI, який показує клієнту поточний стан черги. Щоб уникнути подвійного надсилання електронних листів, сервер також може вести журнал того, кому він надіслав цей електронний лист, і перевіряти кожну адресу проти цього, щоб переконатися, що він ніколи не надсилає дві адреси на одну і ту ж адресу, навіть якщо ви двічі надсилаєте один і той же список на черга.

Вибираючи URI для чого завгодно, намагайтеся думати про це як результат, а не про дію. Наприклад, google.com/search?q=dogsпоказує результати пошуку слова "собаки". Він не обов'язково виконує пошук.

Справи №3 та №4 у вашому списку також не є безсильними діями. Ви вважаєте, що різні запропоновані ефекти можуть вплинути на дизайн API. У всіх чотирьох випадках я використовував би один і той же API, оскільки всі чотири змінили «світову державу».


Скажімо, акція полягає в тому, щоб перебирати гігантську чергу електронної пошти та надсилати повідомлення купу людей. Це ідентичне? Чи є ідентичні дії для PUT або POST?
Кірк Оуімет

@kirk Я розширив свою відповідь.
Ніколас Шенкс

0

Дивіться мою нову відповідь - вона суперечить цій і пояснює REST та HTTP більш чітко та точно.

Ось рекомендація, яка, можливо, є ВИПУСКОЮ, але це, звичайно, не єдиний варіант. Щоб почати бракувати, коли служба отримує запит:

POST /v1/dogs/1/bark-schedule HTTP/1.1
...
{"token": 12345, "next": 0, "frequency": 10}

token - це довільне число, яке запобігає надлишковим гавкам незалежно від того, скільки разів цей запит надсилається.

nextвказує час наступної кори; значення0 означає "ASAP".

Щоразу, коли ви GET /v1/dogs/1/bark-scheduleмаєте отримувати щось подібне, де t - час останньої кори, а u - t + 10 хвилин:

{"last": t, "next": u}

Я настійно рекомендую використовувати ту саму URL-адресу, щоб подати запит на гавкіт, який ви використовуєте, щоб дізнатися про поточний стан гавкання собаки. Для REST це не суттєво, але він підкреслює акт зміни розкладу.

Відповідний код статусу, мабуть, становить 205 . Я уявляю клієнта, який дивиться на поточний графік, POSTз тією ж URL-адресою, щоб змінити його, і служба доручає надати розкладу другий погляд, щоб довести, що він був змінений.

Пояснення

ВІДПОВІДНИЙ

Забудьте про HTTP на мить. Важливо розуміти, що ресурс - це функція, яка вимагає часу як введення та повертає набір, що містить ідентифікатори та подання . Давайте спростимо це до: ресурс - це набір R ідентифікаторів та представлень; R може змінюватися - члени можуть бути додані, видалені або змінені. (Хоча це погано, нестабільна конструкція для видалення або модифікації ідентифікаторів.) Ми говоримо, що ідентифікатор, який є елементом R, ідентифікує R , а представлення, яке є елементом R, являє собою R .

Скажімо, R - собака. Ви випадково ідентифікуєте R як /v1/dogs/1. (Значення /v1/dogs/1є членом R ). Це тільки один з багатьох способів , ви могли б визначити R . Ви також можете визначити R як /v1/dogs/1/x-raysі як/v1/rufus .

Як ви представляєте R ? Можливо, з фотографією. Можливо, з набором рентгенівських променів. А може, із зазначенням дати та часу, коли R востаннє гавкав. Але пам’ятайте, що це все представлення одного ресурсу . /v1/dogs/1/x-raysє ідентифікатором того самого ресурсу, який представлений відповіддю на запитання "коли зробив R останній раз брахав?"

HTTP

Декілька представлень ресурсу не дуже корисні, якщо ви не можете звернутися до потрібного. Ось чому HTTP корисний: він дозволяє підключати ідентифікатори до представлень . Тобто це спосіб сервісу отримати URL-адресу та вирішити, яке представництво потрібно обслуговувати клієнту.

Принаймні, це і GETробить. PUTв основному зворотне GET: ви PUTпредставляєте r за URL-адресою, якщо хочете, щоб майбутні GETзапити на цю URL-адресу повернули r , з деякими можливими перекладами, такими як JSON в HTML.

POSTє більш слабким способом зміни представлення. Подумайте, чи є логіка відображення та логіка модифікації, які є аналогами один одному - обидва відповідають одній і тій же URL-адресі. Запит POST - це запит на логіку модифікації для обробки інформації та зміни будь-яких представлень (не лише представлення, розташованих за тією ж URL-адресою), як вважає за потрібне службу. Зверніть увагу на третій абзац після 9.6 PUT : ви не замінюєте річ у URL-адресі новим вмістом; Ви вимагаєте, щоб річ за URL-адресою обробляла деяку інформацію та розумно відповідала у вигляді інформативних уявлень.

У нашому випадку ми просимо логіку модифікації за адресою /v1/dogs/1/bark-schedule(яка є аналогом логіки відображення, яка повідомляє нам, коли вона востаннє бракує і коли буде наступна кора), щоб обробити нашу інформацію та змінити деякі уявлення відповідно. У відповідь на майбутні GETs, логіка відображення, відповідна тій же URL-адресі, підкаже нам, що собака зараз гавкає, як ми хотіли.

Подумайте про роботу cron як про деталі реалізації. HTTP займається переглядом та зміною представлень. Відтепер сервіс повідомляє клієнту, коли собака востаннє гавкала і коли вона буде брахати далі. З точки зору служби, це чесно, тому що ті часи відповідають минулим і запланованим робочим місцям.


-1

REST - стандарт, орієнтований на ресурси, це не дія, як рушій.

Якщо ви хочете , щоб ваш сервер кори , ви повинні дивитися на різні ідеї , як JSON-RPC , або в WebSockets зв'язок.

Кожна спроба зберегти його RESTful зазнає невдачі , на мій погляд: ви можете видати POSTз actionпараметром, ви не створюєте яких - або нових ресурсів , але , як ви можете мати побічні ефекти, ви безпечніше.


POSTбув розроблений для "надання блоку даних ... до процесу обробки даних" . Здається, багато людей відрізняють ресурси від дій, але насправді дії - це лише тип ресурсу. Виклик ресурсу дії на сервері все ще є єдиним інтерфейсом, кешованим, модульним та масштабованим. Це також без громадянства, але це може бути порушено, якщо клієнт призначений очікувати відповіді. Але виклик "методу недійсності" на сервері - це те, що Рой Філдінг задумав разом з REST .
Джейкоб Стівенс

Як я описую у своїй відповіді , ви можете в REST неявно змусити сервер виконати дію, попросивши його сказати: "Ваша дія завершена", тоді як RPC заснований на ідеї просто попросити сервер виконати дія. Обидва мають ідеальний сенс, так само як імперативне та декларативне програмування мають сенс.
Йордан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.