REST - 可撤销的删除操作

8
我有一个关于HTTP DELETE和REST的问题。我有一个资源x,根据x的状态,删除x会做以下两种情况之一:
  1. 永久删除x
  2. x标记为已删除。这意味着x可以在以后被还原。
我认为HTTP DELETE必须根据HTTP / REST规范删除资源,而不是将其标记为已删除。例如:在处理HTTP DELETE后,对x的GET请求必须返回404。这意味着HTTP DELETE不能用于第二种情况。你如何以RESTful的方式建模这个删除行为(1和2)?
然后,由于某些资源可以还原,因此也应通过REST API实现还原。你如何以RESTful的方式建模还原行为?
为简单起见,假设x位于http://company/api/x/上。

可能是RESTful undelete的重复。 - Jonathan Hall
3个回答

5
您可以使用垃圾桶方法。
DELETE http://company/api/x/

将 x 移动到垃圾箱。这时,可以使用以下方式访问它:

GET http://company/api/trashcan/x/

如果您想恢复它,那么请获取检索到的表示,并执行以下操作。
PUT http://company/api/x/

更新:

使用超媒体可以使客户端更清楚地知道他们应该做什么。

GET http://company/api/trashcan/x
=>
200 OK

<x-resource>
  <description>This is the object I deleted</description>
  <link rel="restoreto" href="http://company/api/x/" />
</x-resource>

经过进一步思考,PUT方法确实是正确的方法。它不安全,幂等,并且你知道恢复文件的URI。它完美地符合PUT的语义。
与rel="restoreto"相对应的替代方法可能是rel="originallocation"。


1
我之前考虑过这个方法,但我并不是很喜欢它。我会尽量解释一下原因。前两步是可以的,服务器基本上将资源移动到另一个位置。但是,我对第三步有问题。在那里,你又在初始URI处添加了该资源(顺便说一下,这不应该是POST而不是PUT吗?我认为PUT只适用于更新?)。从概念上讲,这并不是真正意义上的_还原_。这只是_添加_。撤消功能是在客户端实现的,而不是在服务器端。我认为这是一个重要的区别。 - Zure Citroen
@zune PUT可以用于创建,只要客户端事先知道URL即可。我理解你关于还原与添加的问题。让我考虑一下,看看能否想出更好的解决方案。 - Darrel Miller
我看了你的更新,谢谢。虽然如此,我仍然不是很信服。我同意使用超媒体可以使其更清晰,但我的最初论点仍然成立:客户端负责还原,他自己必须再次PUT资源。这也意味着他可以在PUT中提供x的不同版本,这些版本根据“还原”的定义是不正确的。这意味着服务器在处理PUT时必须验证接收到的内容与原始内容完全相等。 - Zure Citroen
1
@Zune 好的,那很有道理。那么你可以退而求其次使用处理资源。POST /FileRevertProcessor?url=/company/api/x - Darrel Miller

3
您有一个资源,可以存在于多个状态中(活动的、标记为删除的、已删除的),并且具有将资源从一种状态转移到另一种状态的操作。这是状态机的经典定义。对于给定的转换,您会问自己“哪个HTTP动词代表特定的转换?我是否必须滥用其中一个?POST是一个万能的吗?我可以发明自己的动词吗?”有更好的方法。直接公开状态,并使用GET和PUT来修改它。让服务器想办法从原始状态到达新状态。例如,您可能有一个资源:
GET /foo -> {"a": 1, "b": 2, "status": "active"}

你想要改变它,使之成为:

GET /foo -> {"a": 1, "b": 2, "status": "marked"}

你可能会想知道是否应该使用DELETE方法,或者自定义方法,或者只需PUT新状态即可完成:

GET /foo -> {"a": 1, "b": 2, "status": "active"}
PUT /foo <- {"a": 1, "b": 2, "status": "marked"}
GET /foo -> {"a": 1, "b": 2, "status": "marked"}

这是一个非常好的和透明的解决方案。不错!如果用户提供无效的状态转换,我猜会返回405错误代码?我唯一能找到的缺点是PUT要求提供完整的资源,而不仅仅是增量(这就是为什么你在PUT中添加了字段'a'和'b') 。HTTP PATCH何时才能广泛传播呢?;) - Zure Citroen
如果您不想发送完整的表示形式,那么PATCH是一个不错的选择。另一个选择是在/foo/status上公开单独的资源。 - fumanchu
如果用户提供的状态从当前状态没有定义转换,我建议使用409。 - fumanchu
我创建了一个新的帖子来扩展这个模型。请参见https://dev59.com/NVnUa4cB1Zd3GeqPaneP。 - Zure Citroen

0

好的,说得对,我没有意识到该协议是开放的REST。有两个问题:(1)假设资源的状态表示该资源只能标记为已删除,那么当HTTP DELETE调用该资源时(或者反过来),您会返回什么? 403还是405还是其他什么? (2)POST发送到哪个资源? 直接发送到http://company/api/x还是其他什么? - Zure Citroen
同时,返回403是保留的HTTP响应(权限不足)。您可以返回标准的HTTP响应,或者创建一个自己序列化并通过JSON返回的响应对象。RESTful实现通常规定使用动词和轻量级XML或JSON。这基本上就是REST的要点。 - Jonathan
非常感谢您的回答。您对问题2的回答很有启发性。我对问题1不是很清楚,我会再试一次(抱歉,英语不是我的母语)。假设资源只能标记为已删除。也就是说,用户无法删除它。当用户调用HTTP DELETE时,您会怎么做?405? - Zure Citroen
不要提供HTTP DELETE请求。只需创建一个POST请求,使用时间戳/审计跟踪标记记录以进行删除,并返回成功的200状态码或失败的500状态码。 - Jonathan
如果资源不允许使用DELETE方法,不要使用403。正确的响应应该是405。请参考http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-15#section-8.4.4。 - fumanchu
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接