Подтвердить что ты не робот

RESTful атомное обновление нескольких ресурсов?

Представьте, что веб-приложение хранит некоторый ресурс данных с некоторым идентификатором, который хранит три вложения (например, pdf) для каждого элемента данных.

Схема URL

data/{id}/attachment1
data/{id}/attachment2
data/{id}/attachment3

Существует API RESTful для вложений, предоставляющих операции GET/PUT/DELETE, реализующие операции CRUD на стороне сервера.

Если id равен 123, я хотел бы выполнить операцию, в которой

  • attachment1 заменяется новым вложением (таким образом, что GET file/123/attachment1 возвращает новое вложение)
  • attachment2 удаляется (так что GET file/123/attachment2 возвращает 404)
  • attachment3 остается неизменным.

Обновление должно быть атомарным - полное обновление выполняется сервером или вообще ничего.

Применение простых PUT file/123/attachment1 и DELETE file/123/attachment2 не является атомарным, так как клиент может потерпеть крах после PUT, и на сервере нет намека на то, что он должен сделать откат в этом случае.

Итак, как мне выполнить операцию с помощью RESTful?

Я думал о двух решениях, но оба они не кажутся 100% RESTful:

  • Используйте PATCH (может быть PUT, но PATCH лучше отражает семантику частичное обновление) с multipart/form-data по данным /123: multipart/form-data - это последовательность объектов, состоящих из нового "application/pdf", связанное с полем "attachment1" и то, что будет представлять нулевое значение для обозначения удаления attachment2.

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

  • Используйте ресурс, представляющий транзакцию. Я мог бы отправить данные ID 123 URL-адрес транзакции, который будет создавать ресурс транзакции представляющий копию текущего состояния хранимого ресурса данных на сервере, например. транзакции/данные/123. Теперь я могу вызвать PUT и УДАЛИТЬ на вложения этого временного ресурса (например, DELETE transaction/data/123/attachment2) и сообщить фиксация этой версии ресурса на сервере через PUT on транзакции/данные/123. Это обеспечивает атомарность, когда приходится реализовать дополнительную логику на стороне сервера для работы с несколькими клиентами изменение того же ресурса и разбитых клиентов, которые никогда не совершались.

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

Я как бы застрял здесь, поэтому любые идеи были бы полезны, спасибо!

4b9b3361

Ответ 1

Вы хотите использовать второй вариант, вариант транзакции.

Что вам не хватает, так это создание транзакции:

POST /transaction

HTTP/1.1 301 Moved Permanently
Location: /transaction/1234

Теперь у вас есть ресурс транзакции, который является гражданином первого класса. Вы можете добавить к нему, удалить из него, запросить его текущее содержимое, а затем, наконец, выполнить его или удалить (т.е. Откат) транзакцию.

Пока транзакция выполняется, это еще один ресурс. Здесь нет состояния клиента. Любой пользователь может добавить к этой транзакции.

Когда все это сделано, сервер применяет изменения все сразу, используя какой-то внутренний механизм транзакций, который находится вне области действия.

Вы можете записывать такие вещи, как Etags и if-modified заголовки в подзаголовках транзакций, чтобы, когда они все применяются, вы знаете, что что-то не изменилось за вашей спиной.

Ответ 2

Очень интересный вопрос. Профессор C.S. в университете Лугано (Швейцария) написал несколько слайдов об этой ситуации:

http://www.slideshare.net/cesare.pautasso/atomic-transactions-for-the-rest-of-us

Однако я не уверен, что предоставляемое им решение полностью RESTful, потому что на серверной стороне оно не похоже на апатию.

Будучи честным, поскольку сама транзакция состоит из нескольких состояний, я не думаю, что для этой проблемы может быть полностью RESTful решение.

Ответ 3

Предполагая, что ваши URI являются иерархическими:

PUT data/{id}
[attachment2,attachment3]

Часть вашей проблемы заключается в том, что attachment1/2/3 является ужасным идентификатором. Индекс никогда не должен быть частью ваших URI.

Ответ 4

Я не испытываю, но у меня есть идея для решения, поскольку я сталкиваюсь именно с этой проблемой в разработке.

Во-первых, я использую аналогию со мной (клиентом), отправляя сообщение в Fred1 в доме (сервер с ресурсами), что я хочу, чтобы он выключил переключатель освещения (изменение состояния части ресурса) и включил чайник (изменение состояния другой части ресурса). После отключения выключателя света Фред, к сожалению, имеет сердечный приступ.

Теперь у меня нет ничего от Фреда, чтобы сказать, сделал ли он то, что я спросил. Фреда заменяет другой Фред. Сообщение, которое я отправил, не получил ответа. Единственный способ, которым я могу продолжить, - спросить Fred2, если выключатель света выключен, и чайник включен (ресурс находится в состоянии, которое я ожидал бы после того, как я попросил его сделать что-то для меня). Это неудачное состояние дел (ошибка) и добавляет к моей нагрузке, но теперь я могу исходить из того, что я знаю, что сделал Фред1 перед его сердечным приступом. Я могу либо вернуться к чертежной доске (информировать пользователя о том, что что-то пошло не так, и нам нужно его переделать), либо внести изменения, которые завершили бы мой запрос, если это все еще актуально (включите чайник).

Это начало того, как я это сделаю, очевидно, что существует проблема с re-областью, но если я уже определил свою область (меня интересует только переключатель света и чайник), тогда у меня должно быть достаточно информации ( зная состояние переключателя света и чайник), чтобы дать новую команду Fred2, не возвращаясь к пользователю для инструкций.

Как это звучит?