RESTful原子更新多个资源?

时间:2012-01-29 19:22:55

标签: rest transactions crud atomic multipartform-data

想象一个网络应用程序存储一些数据资源,其中一些id存储每个数据的三个附件(例如pdf)。

网址方案是

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

存在用于在服务器端实现CRUD操作的GET / PUT / DELETE操作的附件的RESTful API。

让id为123,我想执行

操作
  • attachment1被新附件替换(以便GET file/123/attachment1返回新附件)
  • attachment2已删除(以便GET file/123/attachment2返回404)
  • attachment3保持不变。

更新应该是原子的 - 完整更新由服务器执行或根本不执行。

应用简单的PUT file/123/attachment1DELETE file/123/attachment2不是原子的,因为客户端可能在PUT之后崩溃,并且服务器没有提示他应该在这种情况下进行回滚。

那么如何以RESTful方式实现操作呢?

我想过两个解决方案,但它们似乎都不是100%RESTful:

  • 使用PATCH(可能是PUT,但PATCH更能反映出语义 对数据/ 123上的multipart / form-data进行部分更新: multipart / form-data是一个由new组成的实体序列 “application / pdf”与“attachment1”字段相关联 表示删除的空值的东西 attachment2。

虽然这确保了原子性,但我怀疑这是RESTful,因为我使用不同的参数列表重载PATCH方法,这违反了统一接口约束。

  • 使用代表交易的资源。我可以发布数据ID 123 到将创建事务资源的事务URL 表示存储的数据资源的当前状态的副本 在服务器上,例如交易/数据/ 123。现在我可以打电话给PUT和 删除此临时资源的附件(例如DELETE transaction/data/123/attachment2)并进行通信 通过PUT将此版本的资源提交给服务器 交易/数据/ 123。这确保了必要时的原子性 实现额外的服务器端逻辑来处理多个客户端 更改相同的资源并崩溃从未提交的客户端。

虽然这似乎与REST一致但似乎违反了无国籍的约束。事务资源的状态不是服务状态而是应用程序状态,因为每个事务资源都与单个客户端相关联。

我有点被困在这里,所以任何想法都会有所帮助,谢谢!

4 个答案:

答案 0 :(得分:16)

您想使用第二个选项,即交易选项。

您缺少的是创建交易:

POST /transaction

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

现在您拥有一个一等公民的交易资源。您可以添加它,从中删除,查询以查看其当前内容,然后最终提交或删除(即回滚)事务。

当交易正在进行时,它只是另一种资源。这里没有客户状态。任何人都可以添加到此交易中。

完成所有操作后,服务器会使用一些超出范围的内部事务机制一次性应用所有更改。

您可以在事务子操作中捕获Etags和if-modified标头之类的内容,这样当它们全部应用时,您就会知道背后的某些内容没有发生变化。

答案 1 :(得分:5)

非常有趣的问题。卢加诺大学(瑞士)的C.S.教授写了一些关于这种情况的幻灯片:

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

但是我不确定他提供的解决方案是完全REST的,因为它在服务器端似乎没有真正的无状态。

老实说,由于交易本身是由多个州组成的,我不认为这个问题可以有一个完全RESTful的解决方案。

答案 2 :(得分:0)

假设您的URI是分层的:

PUT data/{id}
[attachment2,attachment3]

你的部分问题是attachment1 / 2/3是一个糟糕的标识符。索引永远不应该是您的URI的一部分。

答案 3 :(得分:0)

我没有经验,但我有一个解决方案的想法,因为我在开发过程中遇到了这个问题。

首先,我使用我(客户端)的类比向房子(带资源的服务器)中的Fred1发送消息,我希望他关闭电灯开关(更改部分资源的状态)并打开水壶(改变资源另一部分的状态)。不幸的是,在关掉电灯开关后弗雷德心脏病发作了。

现在我从弗雷德那里得到的一切都没有说他是否做了我的要求。 Fred被另一个Fred取代。我发来的消息没有得到答复。我唯一能做的就是问Fred2,如果电灯开关关闭,水壶开启(资源处于我要求他为我做的事情之后的状态)。这是一个不幸的事态(错误),并增加了我的工作量,但我现在可以继续我知道Fred1在心脏病发作前做了什么。我可以回到绘图板(告知用户出现了问题,我们需要重新进行操作)或者进行更改以完成我的请求(如果仍然相关)(打开水壶)。

这是我如何做的开始,显然有关于重新范围,但如果我已经定义了我的范围(我只对灯开关和水壶感兴趣)那么我应该有足够的信息(知道灯开关和水壶的状态)给Fred2一个新的命令而不返回给用户进行指令。

听起来怎么样?