HTTPQUERY
English

412 Precondition Failed

用法

收到 412 Precondition Failed 错误消息时,客户端知道请求中指定的一个或多个条件未满足,资源未处于预期状态。具体行为取决于前置条件头。If-Match 失败对任何方法都返回 412。If-None-Match 失败对 GET 和 HEAD 返回 304,对所有其他方法返回 412。

这与 304 类似,不过在 304 的情形中,之所以未发送资源,是因为客户端已拥有可用的最新版本,无需再次传输。

SEO 影响

像 Google 这样的搜索引擎不会索引响应状态为 412 的 URL。此前已被索引的 URL 会从搜索结果中移除。

示例

客户端想要回复一篇博客帖子,但前提条件是自客户端上次查看该资源以来帖子未发生变化。该条件包含在 If-Unmodified-Since 头中。当条件不满足时,说明资源已被修改,服务器返回 412 Precondition Failed,表明请求未完成。

通过遵循此协议,可以避免”更新丢失”问题。更新丢失发生在多个客户端向同一资源写入、且其中一个或多个客户端使用了过时版本时。通过确保资源未被修改,可以防止覆盖在此期间已被更改的数据。

请求

POST /blog/update?postid=111&task=reply HTTP/1.1
Host: example.com
If-Unmodified-Since: Fri, 1 Jan 2021 00:00:00 GMT
Content-Type: text/plain
Content-Length: 45

<Message body contains reply-text from the client>

响应

HTTP/1.1 412 Precondition Failed
Content-Type: text/html
Content-Length: 139

<html>
  <head>
    <title>Blog Update Error</title>
  </head>
  <body>
   <p>The post has changed since last viewed.</p>
  </body>
</html>

如何修复

用 GET 或 HEAD 请求获取资源的当前版本,以取得最新的 ETag 或 Last-Modified 值。用当前值替换 If-Match 或 If-Unmodified-Since 头中过时的校验器,然后重试请求。

使用乐观并发控制时(在 REST API 和 CMS 平台中很常见),工作流程如下:

清除浏览器缓存可解决由陈旧的缓存校验器导致的 412 错误。浏览器持有过时的 ETag 或时间戳,并会自动为请求附加旧的条件头。

客户端与源服务器之间的缓存代理和 CDN 有时会提供陈旧的校验器。核实代理配置在传递条件头时不加修改。例如,AWS CloudFront 在 GET 请求上的 If-Match 头与对象的 ETag 不匹配时返回 412。在这种情况下,确认 CDN 缓存是新鲜的,或使该分发失效。

412 与 409 对比

412 和 409 都表示状态冲突,但成因不同。412 响应意味着某个前置条件头(If-Match 或 If-Unmodified-Since)求值为假。409 响应意味着请求与资源当前状态冲突,与条件头无关。将 412 用于由 ETag 或时间戳驱动的乐观并发检查。将 409 用于业务逻辑冲突,例如重复条目或无效的状态转换。

代码参考

.NET

HttpStatusCode.PreconditionFailed

Rust

http::StatusCode::PRECONDITION_FAILED

Rails

:precondition_failed

Go

http.StatusPreconditionFailed

Symfony

Response::HTTP_PRECONDITION_FAILED

Python3.5+

http.HTTPStatus.PRECONDITION_FAILED

Java

java.net.HttpURLConnection.HTTP_PRECON_FAILED

Apache HttpComponents Core

org.apache.hc.core5.http.HttpStatus.SC_PRECONDITION_FAILED

Angular

@angular/common/http/HttpStatusCode.PreconditionFailed