If-Match 是什么
If-Match 是条件请求头:客户端把自己上次读到的 ETag 带上,服务器只有在当前资源的 ETag 与之匹配时才执行这次写操作,否则返回 412 Precondition Failed。这是 HTTP 层的乐观锁——两个人同时编辑同一条记录时,后提交的人会收到 412,从而避免「后来者静默覆盖前者」的数据丢失。
基本信息
| 方向 | 请求头 |
|---|---|
| 取值 | ETag 列表,或通配符 "*" |
| 比较方式 | 强校验:弱 ETag(W/ 前缀)不参与匹配 |
| 不匹配时 | 返回 412 Precondition Failed |
| 规范 | RFC 9110 §13.1.1 |
| 配合头 | ETag、If-None-Match、428 Precondition Required |
典型使用场景
- 文档协作或配置管理:防止并发编辑互相覆盖
- 订单状态流转:确保读取到的是同一个版本再更新
- 幂等写入保护:只在资源未被改动时才覆盖,避免重放请求破坏数据
- 用 If-Match: * 表达「资源必须已存在」,否则视为失败
- 服务端用 428 强制要求客户端带条件头,杜绝无条件的覆盖写
乐观锁更新流程
乐观锁更新流程
# 1) 先读取资源并记下 ETag
ETAG=$(curl -sI https://api.example.com/docs/7 | awk 'tolower($1)=="etag:"{print $2}' | tr -d '\r')
# 2) 带条件提交更新,只有版本一致才会成功
curl -si -X PUT https://api.example.com/docs/7 \
-H "If-Match: $ETAG" -H 'Content-Type: text/markdown' \
--data-binary @doc.md
# 3) 版本已被他人改动 → 服务端返回 412,客户端应重新拉取合并响应与处理
| 服务端行为 | 返回码 | 客户端应做什么 |
|---|---|---|
| ETag 匹配,更新成功 | 200 或 204 | 更新本地缓存的 ETag |
| ETag 不匹配 | 412 Precondition Failed | 重新获取资源,合并后再提交 |
| 缺少 If-Match 而服务端要求 | 428 Precondition Required | 改为带条件头重试 |
| 资源不存在 | 404 Not Found | 确认路径或先创建资源 |
| 使用弱 ETag 做匹配 | 412(强校验不通过) | 改用强 ETag 或去掉 W/ 前缀 |
If-Match 用的是强校验:带 W/ 前缀的弱 ETag 不能用于匹配,因为弱 ETag 只保证语义等价、不保证字节一致,用它做并发控制会漏掉真实的冲突。
如果服务端只支持 If-Match 但客户端默认不带条件头,那么并发覆盖依然会发生。真正的防线是服务端返回 428 强制要求条件头,而不是依赖客户端自觉。