我最近花了很多时间阅读HTTP 1.1规范并将其与REST相关联。我发现HTTP DELETE方法在其“幂等性”和安全性方面有两种解释。以下是两个阵营:
如果您使用HTTP DELETE删除资源,并且成功(200 OK),然后您尝试删除该资源N次,则应该为每个资源取回成功消息(200 OK)每一个删除调用。这是它的“幂等性”。
如果删除具有HTTP DELETE的资源,并且成功(200 OK),然后再尝试删除该资源,则应该返回错误消息(410 Gone),因为资源已被删除。
规范说DELETE是幂等的,当然,但它也说幂等事件的序列仍然会产生副作用。我真的觉得第二阵营是正确的,第一阵营是误导。我们通过允许客户认为它们是删除以前删除的资源的原因引入了什么“安全”?
第一阵营中有很多人,其中包括几位关于这个问题的作者,所以我想检查是否有一些令人信服的理由,而不是导致人们进入第一阵营的情绪。
答案 0 :(得分:20)
成为幂等并不意味着不允许请求产生副作用(这就是'安全'属性所描述的)。这只是意味着多次发出相同的请求不会导致不同或额外的副作用。
在我看来,后续的DELETE请求应该返回一个错误 - 它仍然是幂等的,因为服务器的状态与只发出一个DELETE请求的状态相同。然后再次返回200 OK状态也应该没问题 - 我不认为是幂等要求为后续DELETE请求返回错误代码 - 只是返回错误状态似乎使对我来说更有意义。
答案 1 :(得分:2)
@MichaelBurr关于幂等性和副作用是正确的。
我的观点是,给定的REST请求涉及2个状态,客户端的状态和服务器的状态。 REST就是在服务器和客户端之间传输这些状态,以便客户端的状态映射到服务器状态的子集,换句话说,子集与服务器保持一致。因为幂等性应该意味着后续的幂等请求不会导致任何一个状态与仅仅发出一次请求的状态不同。使用第一个DELETE,您可以想象服务器删除资源并让客户端知道它也可以删除资源(因为资源“不再存在”)。现在两个状态应该与之前相同,减去已删除的项目。如果客户端在尝试删除项目之后执行任何不同的操作,则从服务器转移到客户端的状态必须包含不同的信息。服务器可以通过资源已被删除的信息稍微不同地做一些事情,但是一旦它响应了某些不同的方法的幂等性就基本上被打破了。
对于幂等函数:
delete(client_state) -> client_state - {item}
delete(delete(client_state)) -> client_state - {item}
delete(client_state) = delete(delete(client_state))
保证此幂等性的最佳方法是服务器的响应是否相同,这意味着客户端状态打破幂等性的唯一方法是在客户端处理响应时存在非确定性或副作用(这可能指向处理响应的错误实现)。
如果客户端和服务器之间存在协议,状态代码存在于正在传输的状态(REST)的表示之外,则可以通知客户端该项“不再存在”(就像它在第一次请求中一样,带有之前被删除的额外评论。客户端对此信息的处理方式尚不清楚,但不应影响最终的客户端状态。但是,状态代码不能用于通信状态,或者更确切地说,如果它在其他情况下也传达状态(例如“你没有删除此项目的权限”或“项目未被删除”),那么有一些引入的含糊不清或混乱。所以,如果你想说DELETE是幂等的并且仍然让服务器的响应依赖于之前相同的DELETE请求,那么至少需要一个很好的理由在通信中引入更多的混淆。
HTTP请求涉及删除方法,因此该函数可能类似于
delete(client_state) = send_delete(client_state) -> receive_delete(client_state)
-> respond_to_delete(informative_state)
-> handle_response(informative_state)
-> client_state - {item}
答案 2 :(得分:0)
Wikipedia将Idempotence定义为以下操作:
可以多次应用,而不会将结果更改为初始应用程序之外。
请注意,他们谈论了操作的result
。对我而言,这包括两者服务器状态和响应代码。
HTTP规范在这个问题上有点模糊。它defines it指定HTTP方法是幂等的:
如果多个相同请求的预期效果与单个请求相同。
如果您将维基百科定义中的effect
解释为result
,那么它们的含义相同。无论如何,我质疑告诉客户资源已被删除的实际好处。
最后一点:Idempotence是根据单个客户定义的。一旦您开始引入其他客户端的并发请求,所有投注都将被取消。您应该使用条件更新标头(例如If-Match-ETag
)来处理此类情况。
重申:您应返回相同的返回代码,无论资源是否已删除,是否已被先前请求删除,或根本不存在。