使用哪种HTTP状态码来拒绝由于乐观锁定失败而发起的PUT请求

27
假设我想要实现一种乐观锁定策略,并使用ETags指示最新的资源状态。这意味着当客户端进行更新时,客户端将使用If-Match标头进行PUT请求。
根据HTTP规范,如果提供给If-Match标头的ETag与资源当前状态不匹配,则服务器必须返回412 Precondition failed
然而,从语义上讲,409 Conflict似乎更接近我想要表达的含义,尤其是它提供了响应中要包含什么信息的指导。
在未能匹配If-Match标头中提供的ETag时,返回409是否非常错误?

2
一个关键点是,409 假定“用户可能能够解决冲突并重新提交请求”的情况。 - Papasmile
如果规范不要求先决条件违规使用412,我会支持409的。:/ - Oliver Drotbohm
1
还要考虑一点:“如果请求没有 If-Match 头字段,会导致除 2xx 或 412 状态之外的任何结果,则必须忽略 If-Match 头。” - Papasmile
这是一个棘手的问题,因为没有头部的请求本质上意味着“无论如何都要存储”,因为没有办法了解冲突情况。因此,它当然会导致 200 的结果。实际上,客户端强制执行 PUT 并覆盖服务器上的资源状态甚至可能是一个有效的情况。 - Oliver Drotbohm
1个回答

39

从您的规范链接中:

如果没有实体标记匹配,或者给出“*”,并且不存在当前实体,则服务器不得执行请求的方法,并且必须返回412(前提条件失败)响应。当客户端希望防止更新方法(例如PUT)修改自上次检索以来已更改的资源时,此行为最有用。

因为规范要求使用HTTP 412(确实使用了“MUST”),而且很明显他们考虑了正在讨论的用例,所以HTTP 412似乎是正确的响应代码。

412也是相当合理的。请求要求有条件地进行更新。 412表示条件失败,因此服务将不会执行它。特别是因为412非常适合条件请求的概念;409似乎与特定类型的拒绝相关联,这种拒绝可能是有条件的,也可能不是有条件的。例如,我可以看到服务在对无条件请求POST具有内部冲突的内容时返回409。

但请参见以下来自规范的内容:

10.4.10 409冲突
由于资源的当前状态与请求存在冲突,因此无法完成请求。只有在预期用户可能能够解决冲突并重新提交请求的情况下才允许使用此代码。响应正文应包含足够的信息,以便用户识别冲突的来源。理想情况下,响应实体将包含足够的信息,以便用户或用户代理程序修复问题;但这可能是不可能的,也不是必需的。
冲突最有可能发生在响应PUT请求时。例如,如果正在使用版本控制,并且被PUT的实体包括与早期(第三方)请求所做的更改冲突的更改,则服务器可能使用409响应来指示无法完成请求。在这种情况下,响应实体可能会以响应Content-Type定义的格式列出两个版本之间的差异列表。
总之,规范似乎要求在条件请求上下文中使用412,同时建议版本冲突是409的主要驱动因素。在无条件请求的情况下,可能会使用409来处理版本冲突。

1
为什么不是428?-状态码表示源服务器要求请求是有条件的。它的典型用途是避免“丢失更新”问题,其中客户端获取资源状态,修改它,并将其放回服务器,同时第三方已经修改了服务器上的状态,导致冲突。通过要求请求具有条件性,服务器可以确保客户端使用正确的副本。使用此状态码的响应应解释如何成功重新提交请求。例如:HTTP/1.1 428 Precondition Required。 - Jimbo

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