条件请求
ETag 强弱比较、Last-Modified 验证器与五个 If-* 字段的求值优先级。
作者 David更新于 第 15 篇(共 22 篇)
ETag
ETag = entity-tag
entity-tag = [ weak ] opaque-tag
weak = %s"W/"
opaque-tag = DQUOTE *etagc DQUOTE
etagc = %x21 / %x23-7E / obs-textETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
ETag: W/"v1.2"逐条约束:
- 双引号是语法的一部分,不可省略。
ETag: abc非法——这是最常见的实现错误。 - 前缀
W/大小写敏感(%s表示区分大小写),且必须紧贴引号。 - 标签内容对客户端完全不透明:不得解析、不得比较大小、不得推断含义。
- 内容不可含
"(0x22)本身。 - 生成方式不限(哈希、版本号、时间戳皆可),但必须在表示改变时改变。
强验证器 vs 弱验证器
强("x") | 弱(W/"x") | |
|---|---|---|
| 保证 | 字节级完全相同 | 语义等价即可,字节可不同 |
可用于 Range | ✅ | ❌ |
| 典型来源 | 内容哈希 | 「内容没实质变化」的人工判断 |
gzip 压缩会改变字节——同一资源的压缩版与非压缩版必须用不同的强 ETag,否则范围请求会拼出损坏的文件。许多服务器在启用动态压缩时错误地保留原 ETag 或简单地加后缀,这是真实存在的数据损坏源。
两种比较算法
| 算法 | 规则 | 用于 |
|---|---|---|
| 强比较 | 两者均为强验证器 且 字节相同 | If-Match、If-Range |
| 弱比较 | 字节相同即可,忽略 W/ 前缀 | If-None-Match |
例:W/"1" 与 W/"1" 弱比较匹配、强比较不匹配;"1" 与 W/"1" 两种比较都不匹配(前缀不同即字节不同)。
Last-Modified
Last-Modified = HTTP-date表示的最后修改时刻,是弱验证器(精度仅到秒,同一秒内的多次修改无法区分)。
- MUST NOT 晚于响应的
Date。 - 仅当满足以下之一时才可当作强验证器:修改时间与
Date相差至少 1 秒,或缓存有其他手段确认该秒内无变更。 - 服务端应同时发
ETag与Last-Modified——前者精确,后者兼容旧客户端与启发式缓存计算。
五个条件字段
If-Match = "*" / #entity-tag
If-None-Match = "*" / #entity-tag
If-Modified-Since = HTTP-date
If-Unmodified-Since = HTTP-date
If-Range = entity-tag / HTTP-dateIf-Match
强比较。用于避免丢失更新(乐观并发控制):
PUT /doc HTTP/1.1
If-Match: "v1"- 匹配 → 正常执行。
- 不匹配 → 412 Precondition Failed,请求不得产生副作用。
*→ 只要资源当前存在就通过。
If-None-Match
弱比较。两种用途:
GET /doc HTTP/1.1
If-None-Match: "v1"- 缓存验证(
GET/HEAD):匹配 → 304 Not Modified;不匹配 → 正常200。 - 避免覆盖(其他方法):
If-None-Match: *表示「仅当资源不存在时才执行」——用于原子创建,已存在则返回412。
If-Modified-Since
仅对 GET/HEAD 有效,且仅在无 If-None-Match 时才求值。
- 资源在该时刻后未修改 →
304。 - 已修改 →
200。 - 日期无效或晚于服务端当前时间 → MUST 忽略该字段。
If-Unmodified-Since
If-Match 的日期版,仅在无 If-Match 时求值。未修改 → 执行;已修改 → 412。
If-Range
见范围请求。仅在有 Range 时求值,强比较。
求值优先级(MUST 按序)
多个条件字段同时出现时,接收方 MUST 按此顺序求值,先命中先返回:
If-Match存在 → 求值。假 →412。If-Match不存在 且If-Unmodified-Since存在 → 求值。假 →412。If-None-Match存在 → 求值。假(即匹配上了)→GET/HEAD返回304,其他方法返回412。- 方法为
GET/HEAD且If-None-Match不存在 且If-Modified-Since存在 → 求值。假 →304。 - 方法为
GET且有Range且If-Range存在 → 求值。真 →206;假 → 忽略Range,返回完整200。
关键推论:ETag 优先于日期。同时发 If-None-Match 与 If-Modified-Since 时,后者被完全忽略——这是有意设计,因为 ETag 更精确。
304 响应的构造
命中缓存验证时:
- MUST NOT 有消息体。
- MUST 带若返回
200时会发的ETag、Last-Modified(若有)。 - SHOULD 带
Cache-Control、Vary、Expires、Date、Content-Location。 - 不应带
Content-Type、Content-Encoding等描述体的字段(无体可描述)。 - 缓存收到后 MUST 用这些头更新已存储副本,再把更新后的完整响应交给客户端。
副作用约束
条件求值为假时,请求 MUST NOT 产生任何副作用。这是 If-Match 能实现并发控制的前提:服务端必须在执行前求值,且求值与执行需相对于其他并发请求保持原子。
下一篇:范围请求——Range、Content-Range 与 206 的构造。