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