RESTful атомарное обновление нескольких ресурсов? - PullRequest
24 голосов
/ 29 января 2012

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

Схема URL-адреса

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

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

При значении идентификатора 123 я хотел бы выполнить операцию, в которой

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

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

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

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

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

Ответы [ 4 ]

16 голосов
/ 06 апреля 2012

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

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

POST /transaction

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

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

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

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

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

5 голосов
/ 29 января 2012

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

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

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

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

0 голосов
/ 18 апреля 2012

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

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

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

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

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

0 голосов
/ 06 апреля 2012

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

PUT data/{id}
[attachment2,attachment3]

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

...