Конечная точка REST для предварительного просмотра перед отправкой

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
Xtreme Biker восстановить Монику
источник
Что именно может быть этот конфиг и как он влияет на 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 только для того, чтобы не вносить изменений в сервер. Вариант 3 нежизнеспособен в моем случае.
Xtreme Biker восстановил Монику
1
@PedroWerneck Можете ли вы расширить это? Мне кажется, что вариант 2 определяет другую сущность (черновик конфигурации) и предоставляет способы взаимодействия с ними без сохранения состояния.
Андрей говорит восстановить Монику
1
@PedroWerneck Состояния с сохранением состояния так же, как сохранение конфигурации на сервере с сохранением состояния. Таким образом, приложение уже имеет состояние с вашей точки зрения, как и все варианты добавления этой функции.
jpmc26
10

Смысл использования конкретных HTTP-глаголов для различных вызовов API в REST состоит в том, чтобы использовать существующие механизмы HTTP и ожидания.

Использование GET в этом случае идет против обоих.

A. Клиент должен включить тело с GET? неожиданный

B. Сервер возвращает другой ответ на запрос get в зависимости от тела? ломает спец и механику кеширования

Если вы боретесь с RESTful вопросами, мое правило - спрашивать себя.

«Как это лучше, чем просто использовать POST для всего?»

Если нет немедленной и очевидной выгоды, используйте стратегию Just Use POST Stupid (JUPS)

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 --

Обратите внимание, что если вы используете O / RM и / или транзакции для каждого запроса с вашей БД, вы можете легко реализовать эту функцию для всех ваших конечных точек, не требуя работы с какой-либо конкретной конечной точкой: если запрос приходит с этой опцией откатите транзакцию / единицу работы вместо ее фиксации.

marstato
источник
@PeterRader хорошая точка, удалилX-
marstato
Пожалуйста. Вы сказали бы, что объект моделирования должен быть представлен как «объект моделирования»?
Питер
Нет; в этом смысл симуляции, не так ли? Значение заголовка также может быть, noneно это - на мой вкус - слишком сильно противоречит природе POSTметода.
Марстато
2

Я бы предложил относиться к этому так же, как вы относитесь к поискам. Я бы установил конечную точку POST, в /api/config/previewкоторой создается новый предварительный просмотр. Затем я установил бы конечную точку PUT или PATCH в api/configзависимости от того, собираетесь ли вы редактировать текущую конфигурацию или просто замените всю конфигурацию (вероятно, в первом случае вы отправляете только что созданный предварительный просмотр).

Allenph
источник
0

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

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

Pysis
источник
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, ...}

С уважением

Питер Рейдер
источник