RESTful atomic update of multiple resources?

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

Схема URL имеет вид

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

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

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

  • вложение1 заменяется новым вложением (таким, что 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 на data/123: multipart/form-data представляет собой последовательность сущностей, состоящую из нового "application/pdf", связанного с полем "attachment1" и нечто, представляющее собой нулевое значение для обозначения удаления вложения2.

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

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

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

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

24
задан mtsz 29 January 2012 в 19:22
поделиться

2 ответа

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

Вам не хватает создания транзакции:

POST /transaction

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

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

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

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

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

16
ответ дан 29 November 2019 в 00:19
поделиться

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

PUT data/{id}
[attachment2,attachment3]

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

0
ответ дан 29 November 2019 в 00:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: