Реалізація схеми команд в API RESTful


12

Я зараз розробляю API HTTP, сподіваюсь зробити його максимально РЕСТЕВНІМ.

Є деякі дії, функціональність яких поширюється на декілька ресурсів, і колись їх потрібно скасувати.

Я подумав собі, це звучить як шаблон команди, але як я можу моделювати його в ресурс?

Я введу новий ресурс під назвою XXAction, як DepositAction, який буде створений через щось подібне

POST /card/{card-id}/account/{account-id}/Deposit
AmountToDeposit=100, different parameters...

це фактично створить новий DepositAction і активує його метод Do / Execute. У цьому випадку повернення 201 створеного HTTP-статусу означає, що дія була успішно виконана.

Пізніше, якщо клієнт бажає переглянути деталі дії, які він може

GET /action/{action-id}

Оновлення / PUT слід заблокувати, я думаю, тому що це не актуально.

А для того, щоб скасувати дію, я подумав використати

DELETE /action/{action-id}

який насправді викликатиме метод скасувати відповідний об'єкт та змінить його статус.

Скажімо, я задоволений лише одним Do-Undo, мені не потрібно переробляти.

Чи підходить цей підхід?

Чи є якісь підводні камені, причини не використовувати їх?

Це зрозуміло з POV клієнтів?


Коротка відповідь, це не REST.
Еван Плейс

3
@EvanPlaice хочете детальніше розібратися в цьому? саме в цьому питання.
Мітір

1
Я б детально пояснив відповідь, але відповідь Гері вже охоплює більшість / усе те, що я додав би. Я кажу, що це не спокій, тому що URI повинні представляти лише ресурси (тобто не дії). Дії обробляються через GET / POST / PUT / DELETE / HEAD. Подумайте про REST як інтерфейс OOP. Мета полягає в тому, щоб зробити API відповідним загальному шаблону та від'єднати його від можливих деталей щодо реалізації.
Еван Плейс

1
@EvanPlaice Добре, я розумію, дякую. Я думаю, це тут заплутано, тому що депозит можна розглядати як іменник і як дієслово ...
Мітір

У цьому випадку URI повинен представляти транзакцію, де дебетування (отримання грошей) та кредитування (надання грошей) - це дії, що здійснюються за допомогою POST-запитів. POST використовується для обох, оскільки кожен раз, коли гроші переміщуються в будь-якому напрямку, це означає, що створюється нова транзакція. У вашому конкретному випадку трансакції відбуваються на рахунку власника картки, тому номер рахунку картки є URI ресурсу.
Еван Плейс

Відповіді:


13

Ви додаєте в заплутаний шар абстракції

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

В якості альтернативи розглянемо цю HTTP-розмову ...

POST / card / {card-id} / account / {account-id} / Депозит

AmountToDeposit = 100, різні параметри ...

201 СТВОРЕНО

Місцезнаходження = / картка / 123 / рахунок / 456 / Депозит / 789

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

УДАЛИТИ / карта / 123 / рахунок / 456 / Депозит / 789

204 НЕ ЗМІСТ

Споживач API знає, що вони мають справу з ресурсом депозиту і здатний визначити, які операції на ньому дозволені (як правило, через OPTIONS в HTTP).

Хоча реалізація операції видалення ведеться через "дії" сьогодні, немає жодної гарантії, що при переміщенні цієї системи з, скажімо, C # на Haskell і збереження переднього кінця, що вторинна концепція "дії" надалі додаватиме значення , тоді як головна концепція депозиту, безумовно, є.

Редагувати, щоб покрити альтернативу УВІДКЛЮВАННЯ та депозит

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

POST / card / {card-id} / account / {account-id} / Трансакція

Сума = -100 , різні параметри ...

201 СТВОРЕНО

Розташування = / картка / 123 / рахунок / 456 / Перехід / 790

Створюється новий ресурс транзакцій, який має точно протилежну суму (-100). Це призводить до балансування рахунку до 0, ігнорування вихідної транзакції.

Ви можете розглянути можливість створення кінцевої точки "утиліти"

POST / card / {card-id} / account / {account-id} / Transaction / 789 / Скасувати <- BAD!

щоб отримати той же ефект. Однак це порушує семантику URI як ідентифікатора, вводячи дієслово. Вам краще дотримуватися іменників у ідентифікаторах і зберігати операції, обмежені дієсловами HTTP. Таким чином, ви можете легко створити постійну посилання з ідентифікатора і використовувати його для GETs тощо.


3
+1 "технічно цього не слід допускати в збалансованій системі обліку". Хтось знає, як рахувати квасолю. Це твердження абсолютно правильне, способом повернення було б створити ще одну транзакцію, яка повертає кошти назад. Записи в головній книзі завжди слід вважати незмінними та постійними після завершення транзакції.
Еван Плейс

Отже, якщо я зміню, у своїх питаннях замість Видалити / дія / ... видалити / внести / депозит / ... це нормально?
Мітір

2
@Mithir Я описував правило бухгалтерського обліку. У стандартній системі бухгалтерського обліку з подвійним записом ви ніколи не знімаєте транзакцій. Колись вчинена історія вважається незмінною, щоб зберегти людей чесними. У вашому випадку ви все ще можете скористатися дією DELETE, але в бек-енді (колишня таблиця бази даних загальної книги) ви додасте ще одну транзакцію, яка представляє кредитування (тобто повернення) грошей назад користувачеві. Я не лічильник бобів (тобто бухгалтер), але це одна із стандартних практик, що викладаються в курсі "Принципи бухгалтерського обліку я".
Еван Плейс

2
(продовження) Журнали бази даних використовують транзакції аналогічним чином. Ось чому можливо копіювати та / або відновлювати набір даних, використовуючи лише журнали. Поки транзакції відтворюються хронологічно, слід мати можливість відновити набір даних з будь-якої точки його історії. Видалення змінності з рівняння забезпечує узгодженість.
Еван Плейс

1
Досить справедливо просто перейменувати його на Transaction.
Гері Роу

1

Основна причина існування REST - стійкість до помилок мережі. З цією метою всі операції повинні бути безсильними .

Базовий підхід здається розумним, але те, як ви описуєте DepositActionстворення, не здається безсильним, що має бути виправлено. Маючи клієнт, надайте унікальний ідентифікатор, який буде використовуватися для виявлення повторюваних запитів. Так творіння змінилося б на

PUT /card/{card-id}/account/{account-id}/Deposit/{action-id}
AmountToDeposit=100, different parameters...

Якщо інша PUT на ту саму URL-адресу зроблена з тим самим вмістом, що і раніше, відповідь все одно повинна бути, 201 createdякщо вміст однаковий, і помилка, якщо вміст відрізняється. Це дозволяє клієнту просто повторно передати запит, коли він не працює, оскільки клієнт не може сказати, чи загубився запит чи відповідь.

Більше сенсу використовувати PUT, оскільки він просто записує ресурс і є ідентичним, але використання POST також не спричинить жодних проблем.

Для перегляду реквізитів транзакції клієнт матиме GETту саму URL-адресу, тобто

GET /card/{card-id}/account/{account-id}/Deposit/{action-id}

і, щоб скасувати його, він може ВИДАЛИТИ його. Але якщо це насправді має щось спільне з грошима, як підказує зразок, я б запропонував ВИПУСКОВИТИ це з доданими "скасованими" прапорами, а не для підзвітності (щоб залишилися сліди створених та скасованих транзакцій).

Тепер вам потрібно вибрати метод створення унікального ідентифікатора. У вас є кілька варіантів:

  1. Раніше випустіть для обміну специфічний для клієнта префікс, який потрібно включити.
  2. Додайте спеціальний запит POST, щоб отримати порожній унікальний ідентифікатор від сервера. Цей запит не повинен бути ідентичним (і справді не може), оскільки невикористані ідентифікатори насправді не створюють проблем.
  3. Просто використовуйте UUID. Усі користуються ними, і, здається, ніхто не має жодних проблем ні з MAC, ні з випадковими.

2
З того, що мені відомо, POST не є безсильним. en.wikipedia.org/wiki/POST_(HTTP)#Affecting_server_state
Мітір

@Mithir: POST не вважається ідентичним; це все ще може бути. Але це правда, що оскільки всі операції REST повинні бути безсилими, POST в основному не має місця в REST.
Ян Худек

1
Я розгублений ... вміст, який я прочитав, і наявна реалізація, з якою я знайомий (ServiceStack, ASP.NET Web API), все говорить про те, що POST має місце в REST.
Мітір

3
У REST ідентифікація призначається ресурсу, а не протоколу або кодам його відповіді. Таким чином, в REST через HTTP методи GET, PUT, DELETE, PATCH і так далі вважаються ідентичними, хоча їх відповідні коди можуть відрізнятися для наступних викликів. POST ідентичний настільки, що кожен виклик створює новий ресурс. Див. Fielding's Добре використовувати POST .
Гері Роу

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