2012-01-29 131 views
22

想象一下,一个网络应用程序存储一些数据资源,这些数据资源有一些id,每个数据存储三个附件(例如pdf)。多个资源的RESTful原子更新?

的URL方案是

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

的REST风格的API存在提供GET/PUT附件/ DELETE在服务器端执行CRUD操作操作。

又让ID是123,想以执行其中

  • 附件1由一个新的附件替换一个操作(使得GET file/123/attachment1返回一个新的附件)
  • attachment2被删除(使得即GET file/123/attachment2返回404)
  • attachment3保持不变。

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

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

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

我想过两种解决方案,但他们似乎都没有100%的RESTful:

  • 使用Patch(可放,但PATCH能更好地反映的 的部分更新的语义)与多/表格数据/ 123:多部分/表格数据是由与字段“attachment1”和 相关联的新的 “application/pdf”组成的实体序列,其将代表空值以表示空值删除 附件2。

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

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

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

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

+2

第二种方法的好处是提供了一个很好的数据更改历史记录,并可能允许您跳过某些日志记录。 – Jasper 2012-04-06 07:07:37

+0

@mtsz我现在正在努力解决这个问题。我喜欢下面选择的答案,但创建短暂的临时生命周期的交易资源似乎有很多工作要做。你认为给原子事务执行一个像“switcheroo”这样的名称并创建一个执行该事务的特定Web服务会很糟糕吗?例如,具有{fileId:123}的主体的POST/doSwitcheroo ....该服务将具有逻辑地以文件形式执行上面在ID为123的文件中描述的动作012 – 2015-11-11 02:22:57

回答

15

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

什么你缺少的是交易的创建:

POST /transaction 

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

现在你有一个交易的资源,是一个一流的公民。您可以添加,删除,查询当前内容,最后提交或删除(即回滚)事务。

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

当全部完成后,服务器将使用一些超出范围的内部事务机制一次性应用更改。

您可以在事务子动作中捕获诸如Etags和if-modified标头之类的东西,这样当它们全部应用时,就会知道背后没有变化。

+0

听起来很合理。在这种情况下,“申请状态”的概念可能有点过分夸大了,而这个“头等公民”是正常的服务状态。 [Richardson&Ruby](http://shop.oreilly.com/product/9780596529260.do)宣传这种解决方案,虽然它应用于多服务器场景。我想知道,Roy Fielding会做什么? :) – mtsz 2012-04-09 18:22:43

+6

您可以像查看购物车一样查看交易。在购物车中,客户随着时间的推移建立他们的交易,然后他们只需完成结账过程,最终以他们“确认”或“取消”订单结束。当你用这些术语来看它时,用这个词汇来说,它更有意义。但在10000英尺时,它们基本上是同样的问题。 “这是我想要做的所有事情的清单 - 现在,去!”购物车绑定到特定用户的事实是安全/身份问题,而不是“状态”与“无状态”问题。 – 2012-04-09 18:30:56

+0

我已经实现了这个很好的解决方案。您可以获得解决“编辑比赛”的好处,而不会免费丢失数据,这是“列表”解决方案无法实现的。 – mtsz 2012-04-19 18:35:01

5

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

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

但是我真的不知道,他提供的解决方案是完全基于REST的,因为它似乎并没有真正上无国籍服务器端。

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

+1

我浏览了他们的论文(朝向RESTful上的分布式原子事务服务)。他们提出一个带有协调器资源的Try-Cancel/Confirm协议,最终确认所有参与资源的变化(或启动先前状态的恢复)。资源是分布式服务器,因此范围稍大于我的范围。尽管如此,这个TCC协议应该与上面提到的第二种解决方案非常相似。至少他们在概念上解决应用程序状态问题,将其转换为服务状态,通过附加方式恢复以前的状态。也许就是这样。 – mtsz 2012-01-30 03:27:16

0

假设你的URI是分层:您的问题

PUT data/{id} 
[attachment2,attachment3] 

部分原因是,附件1/2/3是一种可怕的标识符。索引不应该是你的URI的一部分。

+0

标识符是任意的,可以使场景更通用。设想一个具体的场景,其中有数据blob,并附带预期的模型,描述,图片等。由于PUT是连续的,我不明白您的解决方案如何解决多个连接资源的一致性更新问题? – mtsz 2012-04-06 12:56:17

+0

将附件列表视为资源。你正在推销内容和订单。 1操作。 – noah 2012-04-06 13:37:39

+0

因此,如果我理解正确,您可以选择使用建模顺序的媒体类型,如multipart/form-data或自定义类型。由于我的原始问题包含此解决方案,因此阅读该解决方案的合理性会很有趣。 – mtsz 2012-04-09 18:01:22

0

我没有经验,但我有一个解决方案的想法,因为我正面临开发中的这个问题。

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

现在我已经从弗雷德没有得到任何回应,说他是否做我所问。 Fred被另一个Fred取代。我发送的消息没有收到答复。我可以继续的唯一方法是问Fred2电灯开关是否关闭,电水壶是否打开(在我要求他为我做东西之后,资源处于我期望的状态)。这是一个不幸的事态(错误),并增加了我的工作量,但我现在可以继续,因为我知道Fred1在心脏病发作之前做了什么。我可以回到绘图板(通知用户出现了问题,我们需要重新执行该操作),或者做出可以完成我的请求(如果仍然相关)的更改(打开水壶)。

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

这听起来怎么样?

+0

有趣但有问题:当Fred关闭灯光后发生心脏病时,您的状态会不一致。只要你使用你的客户检查状态并发送另一个消息给下一个Fred(他希望不会死),它就会不一致。如果你的客户在fred死后刚刚死亡会发生什么?那么预期的状态将永远消失。其他客户将面临目前的不一致的状态,这可能会导致严重的麻烦... – mtsz 2012-04-19 18:32:27

+0

确实有趣:)数据库怎么样 - 他们怎么做?一个数据库必须是内部原子的,所以也许这是另一个看起来不错的地方。我正在运行PostgreSQL,因此会检查文档。这是PostgreSQL原子的链接:http://www.postgresql.org/files/developer/transactions.pdf – tentimes 2012-04-20 11:09:48

+0

您可以采用数据库会话的概念(例如sql会话):您在会话中应用的所有修改都是没有执行,直到你提交你的会话。当其中一项修改发生错误时,会话将回滚并保留前一个一致状态。所以当Fred在灯开关打开后死亡时,他抛出一个被捕获的HeartFailureException,触发一次会话回滚,并且灯再次亮起;) – mtsz 2012-04-23 15:37:00