由于乐观锁定失败,使用哪个 HTTP 状态代码来拒绝 PUT

Posted

技术标签:

【中文标题】由于乐观锁定失败,使用哪个 HTTP 状态代码来拒绝 PUT【英文标题】:Which HTTP status code to use to reject a PUT due to optimistic locking failure 【发布时间】:2013-10-07 23:04:17 【问题描述】:

假设我想实现某种乐观锁定并使用 ETags 来指示最新的资源状态。这意味着,当PUTting 进行更新时,客户端将使用If-Match 标头。

根据HTTP spec,如果为If-Match 标头提供的ETag 与资源的当前状态不匹配,则服务器必须返回412 Precondition failed

但是,409 Conflict 似乎更接近我想要在语义上表达的内容,特别是因为它提供了在响应中包含什么内容的指南。

如果无法匹配If-Match 标头中提供的 ETag,则宁可返回 409 是否非常错误?

【问题讨论】:

一个关键是 409 假定“预期用户可能能够解决冲突并重新提交请求的情况。” 如果规范不要求 412 违反先决条件,我无论如何都会支持 409:/. 还要考虑“如果请求在没有 If-Match 标头字段的情况下会导致 2xx 或 412 状态以外的任何状态,则必须忽略 If-Match 标头。” 这是一个棘手的问题,因为没有标头的请求本质上意味着“无论如何都存储”,因为无法找出冲突。所以反过来,它当然会导致200。实际上,客户端决定强制 PUT 并覆盖服务器上的资源状态甚至可能是有效的情况。 【参考方案1】:

从您的规范链接:

如果没有一个实体标签匹配,或者如果给出了“*”并且没有当前实体存在,服务器不能执行请求的方法,并且必须返回一个 412(Precondition Failed)响应。当客户端想要阻止更新方法(例如 PUT)修改自客户端上次检索后已更改的资源时,此行为最有用。

因为规范需要 HTTP 412(实际上它使用“MUST”),并且很明显它们准确地解释了正在讨论的用例,所以 HTTP 412 似乎是正确的响应代码。

无论如何,412 是相当合理的。该请求说有条件地进行更新。 412 表示条件失败,因此服务不会执行此操作。尤其是因为 412 非常适合条件请求的概念; 409 似乎与特定类型的拒绝有关,这种拒绝本质上可能是有条件的,也可能不是。例如,我可以看到服务返回 409 以响应无条件请求 POST 具有内部冲突的内容。

但请参阅以下内容,也来自规范:

10.4.10 409 冲突

由于与资源的当前状态冲突,请求无法完成。仅在预期用户可能能够解决冲突并重新提交请求的情况下才允许使用此代码。响应正文应该包含足够的信息让用户识别冲突的来源。理想情况下,响应实体将包含足够的信息供用户或用户代理解决问题;但是,这可能是不可能的,也不是必需的。

在响应 PUT 请求时最有可能发生冲突。例如,如果正在使用版本控制并且被 PUT 的实体包括对资源的更改,这些更改与早期(第三方)请求所做的更改相冲突,则服务器可能会使用 409 响应来指示它无法完成请求.在这种情况下,响应实体可能会以响应 Content-Type 定义的格式包含两个版本之间差异的列表。

无论如何,规范似乎要求在条件请求上下文中使用 412,同时暗示版本冲突是 409 的关键驱动因素。可能会在版本冲突发生的情况下使用 409 作为无条件请求的一部分。

【讨论】:

为什么不是 428? - 状态码表示源服务器要求请求是有条件的。它的典型用途是避免“丢失更新”问题,即客户端获取资源的状态,对其进行修改,然后将其放回服务器,同时第三方在服务器上修改了状态,从而导致冲突。通过要求请求是有条件的,服务器可以确保客户端使用正确的副本。使用此状态码的响应应该解释如何成功地重新提交请求。例如:HTTP/1.1 428 需要前置条件

以上是关于由于乐观锁定失败,使用哪个 HTTP 状态代码来拒绝 PUT的主要内容,如果未能解决你的问题,请参考以下文章

CoreData:错误:无法解决乐观锁定失败:乐观锁定失败(null),只有旧记录,为啥?

Core Data 乐观锁定失败的精确条件

休眠乐观锁定..它是如何工作的?

对于运行状况检查失败,我应该使用哪个 HTTP 状态代码?

Google Datastore 节点 API 中是不是存在乐观锁定?

如何从Java代码编写乐观和悲观锁定