Кінцева точка 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.