Кінцева точка REST, щоб показати попередній перегляд перед POSTing


17

Я розробляю нову веб-програму, яка працює на основі REST-інтерфейсу та HTML + JS-інтерфейсу.

Існує один метод POST для зміни однієї сутності (назвемо Config), який має кілька побічних ефектів у стані багатьох елементів програми. Припустимо, POST виконується так:

POST /api/config BODY {config: ....}

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

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

GET /api/preview/items BODY {config: ....}

Може показати новий стан для елементів із новою конфігурацією.

GET /api/preview/sales BODY {config: ....}

З новою конфігурацією можна показати новий стан продажів.

Здається, добре використовувати дієслово GET, оскільки я не змінюю стан програми. Однак використання органу запитів із GET- запитами, мабуть, не рекомендується .

Чи є якась добра практика з цього приводу? Іншим вибором може бути збереження конфігурації як чернетки одним методом та відображення результатів разом з іншими, але це потребує додаткового кроку та керування чернетками на сервері:

POST /api/preview/config BODY {config: ....}

GET /api/preview/items?idPreviewConfig=1

Що саме може бути ця конфігурація і як це впливає на itemsабо sales? Чи впливає це на представництво поверненої особи?
Енді

Припустимо, що на товари та продажі впливають зміни, які ви вносите в конфігурацію.
Xtreme Biker відновила Моніку

Але що означають зміни? Чи змінює це набір повернених сутностей? Чи змінює повернуту структуру?
Енді

На насправді він змінює значення itemsі sales(не структура), в залежності від конфігурації ви POST.
Xtreme Biker відновила Моніку

І наскільки точно конфігурація? Може вирости до кількох сотень кілобайт або навіть більше?
Енді

Відповіді:


27

Це занадто домен, щоб мати вбудовану підтримку HTTP.

Натомість ви можете зробити одне з наступного:

  1. Майте POST /api/config/preview. На стороні сервера програма знатиме, що вона не повинна змінювати фактичну конфігурацію, а поєднувати фактичну з тією, яку ви опублікували, і повертати результат із зазначенням того, що було змінено.

    Пізніше, якщо користувач буде задоволений результатом, вона виконає POST /api/configвміст корисного навантаження, як і в попередньому запиті. Це ефективно замінить конфігурацію.

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

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

  2. Майте, POST /api/config/prepareщо запам'ятовує те, що було надіслано у тимчасовому записі, і повертає дві речі: ідентифікатор тимчасового запису (наприклад 12345) та попередній перегляд змін.

    Якщо користувач задоволений результатом, вона виконає POST /api/config/commit/12345остаточне збереження змін. Якщо ні, тимчасовий запис може зберігатися протягом деякого часу, а потім відкинутись за допомогою завдання "cron".

    Перевага полягає в тому, що, знову ж таки, ви можете зберегти оригінал POST /api/configнедоторканим, і клієнти, яким не потрібен попередній перегляд, не зламаються.

    Недоліками є те, що (1) обробка видалення тимчасових записів може бути складною (з-за чого ви думаєте, що однієї години достатньо? Що робити, якщо через десять хвилин у вас не вистачить пам'яті? Як клієнти обробляють HTTP 404 під час виконання комісії запис, термін дії якого закінчився?), і що (2) подання запису в два етапи може бути складнішим, ніж потрібно.

  3. Перемістіть логіку попереднього перегляду на стороні клієнта.


Що щодо надсилання заголовка, який говорить: "Не наполягайте на цьому, покажіть мені лише що" якщо "? Я відредагую це у відповідь, якщо з вами це нормально @ArseniMourzenko
marstato

1
@marstato: особисто я не особливо люблю заголовки HTTP для цього використання. Хоча це може бути сенсом для інших людей, тому я добре, якщо ви відредагуєте мою відповідь. Зауважте, що ви також можете опублікувати свою власну відповідь, що дозволить іншим висловити це (і ви отримаєте бали репутації).
Арсеній Муренко

Я думаю, що варіант 1 краще підходить для мого випадку. Таким чином, ви POST конфігурацію попереднього перегляду і у вас є зміни в результаті, замість того, щоб визначати кінцеві точки попереднього перегляду для кожного з визначених об'єктів. Здається розумним. Єдине, що ви використовуєте POST, щоб не змінювати сервер, технічно кажучи. Варіант 3 в моєму випадку нежиттєздатний.
Xtreme Biker відновила Моніку

1
@PedroWerneck Чи можете ви розширити це? Мені здається, що варіант 2 визначає іншу сутність (проект конфігурації) і надає безгромадянські способи взаємодії з ними.
Ендрю каже, що відновити Моніку

1
@PedroWerneck Це так само, як і зберігання конфігурації на сервері. Тож додаток з вашого погляду вже є надзвичайним, тому є всі варіанти додати цю функцію.
jpmc26

10

Сенс використання конкретних дієслів HTTP для різних api-дзвінків у REST полягає у використанні існуючої механіки та очікувань HTTP.

Використання GET в цьому випадку, здається, суперечить обом.

A. Клієнт повинен включити тіло з GET? несподіваний

B. Сервер повертає іншу відповідь на отримання залежно від тіла? порушує специфікацію та механіку кешування

Якщо ти борешся з ВІДКРИТИМИ питаннями, моє правило - задавати себе.

"Як це краще, ніж просто використовувати POST для всього?"

Якщо немає негайної та очевидної користі, виконайте стратегію Just Use POST Stupid (JUPS)


Хороший улов Хахаха
Xtreme Biker відновив Моніку

@Ewan ... незалежно від того, чи є це прагматичним підходом ... якщо ви використовуєте POST для всього, слід зазначити, що це насправді не RESTful.
Алленф

1
добре, якщо тільки POST не є відповідним вибором для всіх ваших методів. І це не так, як є об'єктивне правило, яке ви можете застосувати, ми просто сперечаємося в наших суб'єктивних тлумаченнях того, що є трохи більше, ніж настанова.
Еван

6

Ви можете надіслати заголовок, який вказує на сервер "не зберігайте цього, покажіть мені, який би результат був, якби ви це зробили". Напр

POST /api/config HTTP/1.1
Host: api.mysite.com
Content-Type: application/json
Persistence-Options: simulate

{
   "config": {
      "key": "value"
   }
}

На що сервер міг би відповісти:

HTTP/1.1 200 OK
Persistence-Options: simulated
Content-Type: application/json

-- preview --

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



@PeterRader хороший момент, видаленоX-
marstato

Прошу. Ви б сказали, що суб'єкт, що знаходиться під моделюванням, повинен бути представлений як "під час моделювання"?
Пітер Радер

Ні; в цьому суть імітації, чи не так? Значення заголовка також може бути, noneале це - на мій смак - занадто суперечить природі POSTметоду.
marstato

2

Я б запропонував ставитись до цього так само, як ви ставитесь до пошуків. Я встановив би кінцеву точку POST, в /api/config/previewякій СТВОРЮЄТЬСЯ новий попередній перегляд. Тоді я встановив би кінцеву точку PUT або PATCH api/configзалежно від того, чи плануєте ви редагувати поточну конфігурацію або просто замінити всю конфігурацію (імовірно, у попередньому випадку ви надсилатимете попередньо створений попередній перегляд).


0

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

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


0

RFC6648 знецінює нові X-конструкції, тому я повинен проголосувати проти ідеї надсилати нове поле заголовка. REST - це стиль архітектури, про що ми говоримо, це RESTful - але нехай це ігнорують на цей момент.

Оскільки REST є репрезентативним (а симуляція не має репрезентації в реальності) і Stateful (а моделювання не є державою, поки її не здійснено), ми повинні мати нову сферу застосування, як сфера моделювання. Але ми повинні називати це емуляцією, а не імітацією, тому що моделювання включає в себе процес моделювання, але стаціонарний означає, що ми маємо стоячий стан, ідеальне рішення моделювання: емуляція. Тому нам потрібно назвати це емуляцією в URL-адресі. Це також може бути хорошим рішенням:

GET  /api/emulation - 200 OK {first:1, last:123}
POST /api/emulation/124 - 200 OK
GET  /api/emulation/124/config - 200 OK {config:{tax:8}}
PUT  /api/emulation/124/config {config:{tax:16}} - 200 OK {config:{tax:16}}
GET  /api/emulation/124/items - 200 OK [first:1, last: 3000]
GET  /api/emulation/124/items/1 - 200 OK {price:1.79, name:'Cup'}
--- show emulation ---
--- commit emulation ---
PUT /api/config {config:{tax:16}}
DELETE /api/emulation/124 - 200 OK

Існує інший підхід .... ви можете помітити, що багато запитів від HTML / JavaScript-клієнта можуть створити Занадто багато запитів , що досягає межі приблизно 17 запитів одночасно (дивіться на цю сторінку ). Ви можете поміняти місцями на використання REST і замість того, щоб доставити кульгаві стани об'єктів, ви можете доставляти багаті сторінки, визначені для користувачів. Приклад:

GET /user/123/config - 200 OK {user:'Tim', date:3298347239847, currentItem:123, 
                  roles:['Admin','Customer'], config:{tax:16}, unsavedChanges:true, ...}

З повагою

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