Skip to main content

2. Validators (验证器)

本规范定义了两种常用于观察资源状态和测试前提条件的元数据形式: 修改日期 (第 2.2 节) 和不透明实体标签 (第 2.3 节)。HTTP 的各种扩展还定义了其他反映资源状态的元数据, 例如 Web 分布式创作和版本控制 (WebDAV, [RFC4918]), 但这些超出了本规范的范围。当资源元数据值在前提条件中使用时, 称为"验证器" (validator)。

2.1. Weak versus Strong (弱与强)

验证器有两种类型: 强验证器或弱验证器。弱验证器易于生成, 但在比较方面的用处要少得多。强验证器非常适合比较, 但可能非常难以高效生成 (有时甚至不可能)。HTTP 不强制所有形式的资源遵循相同强度的验证器, 而是公开正在使用的验证器类型, 并对何时可以将弱验证器用作前提条件施加限制。

"强验证器" (strong validator) 是表示元数据, 只要对 GET 的 200 (OK) 响应的有效负载主体中可观察到的表示数据发生变化, 其值就会改变。

强验证器可能会因表示数据变化以外的原因而改变, 例如当表示元数据的语义重要部分发生变化时 (例如 Content-Type), 但源服务器的最佳做法是仅在必须使远程缓存和创作工具持有的存储响应失效时才更改该值。

缓存条目可能会持续任意长的时间, 无论过期时间如何。因此, 缓存可能会尝试使用它在遥远过去获得的验证器来验证条目。强验证器在与特定资源关联的所有表示的所有版本中随时间推移是唯一的。但是, 这并不意味着在不同资源的表示之间具有唯一性 (即, 同一强验证器可能同时用于多个资源的表示, 并不意味着这些表示是等效的)。

相反, "弱验证器" (weak validator) 是表示元数据, 可能不会对表示数据的每次更改都发生变化。这种弱点可能是由于值计算方式的限制, 例如时钟分辨率, 无法确保资源所有可能表示的唯一性, 或者资源所有者希望按某种自定义的等价集而不是唯一的数据序列对表示进行分组。源服务器应该 (SHOULD) 在认为先前的表示不可接受作为当前表示的替代品时更改弱实体标签。换句话说, 只要源服务器希望缓存使旧响应失效, 弱实体标签就应该更改。

强验证器可用于所有条件请求, 包括缓存验证、部分内容范围和"丢失更新"避免。弱验证器仅在客户端不需要与先前获得的表示数据完全相等时才可用, 例如验证缓存条目或将 Web 遍历限制为最近的更改时。

2.2. Last-Modified (最后修改时间)

响应中的 Last-Modified 头字段提供一个时间戳, 指示源服务器认为所选表示最后修改的日期和时间, 该时间在处理请求结束时确定。

Last-Modified = HTTP-date

使用示例:

Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT

2.2.1. Generation (生成)

源服务器应该 (SHOULD) 为可以合理且一致地确定最后修改日期的任何所选表示发送 Last-Modified, 因为它在条件请求和评估缓存新鲜度 ([RFC7234]) 中的使用可显著减少互联网上的 HTTP 流量, 并且可能是提高服务可扩展性和可靠性的重要因素。

源服务器应该 (SHOULD) 在尽可能接近生成响应的 Date 字段值的时间获取表示的 Last-Modified 值。这允许接收者准确评估表示的修改时间, 特别是当表示在生成响应的时间附近发生变化时。

带有时钟的源服务器绝对不能 (MUST NOT) 发送晚于服务器消息发起时间 (Date) 的 Last-Modified 日期。如果根据源服务器的时钟, 最后修改时间是从评估为未来某个时间的特定于实现的元数据中得出的, 那么源服务器必须 (MUST) 用消息发起日期替换该值。这可以防止未来的修改日期对缓存验证产生不利影响。

2.2.2. Comparison (比较)

Last-Modified 时间在请求中用作验证器时, 除非可以使用规范中定义的特定规则推断它是强验证器, 否则它隐式地是弱验证器。

2.3. ETag (实体标签)

响应中的 ETag 头字段提供所选表示的当前实体标签, 该标签在处理请求结束时确定。实体标签是一个不透明的验证器, 用于区分同一资源的多个表示, 无论这些多个表示是由于资源状态随时间推移而变化、内容协商导致多个表示同时有效, 还是两者兼而有之。

ETag = entity-tag

entity-tag = [ weak ] opaque-tag
weak = %x57.2F ; "W/", 区分大小写
opaque-tag = DQUOTE *etagc DQUOTE
etagc = %x21 / %x23-7E / obs-text

示例:

ETag: "xyzzy"
ETag: W/"xyzzy"
ETag: ""

实体标签可以是弱验证器或强验证器, 默认为强验证器。如果源服务器为表示提供实体标签, 并且该实体标签的生成不满足强验证器的所有特征 (第 2.1 节), 则源服务器必须 (MUST) 通过在其不透明值前添加 W/ (区分大小写) 将实体标签标记为弱验证器。

2.3.1. Generation (生成)

实体标签背后的原则是, 只有服务作者足够了解资源的实现, 才能为该资源选择最准确和最高效的验证机制, 并且任何此类机制都可以映射到简单的八位字节序列以便于比较。由于该值是不透明的, 因此客户端无需知道每个实体标签是如何构造的。

源服务器应该 (SHOULD) 为可以合理且一致地检测到更改的任何所选表示发送 ETag, 因为实体标签在条件请求和评估缓存新鲜度 ([RFC7234]) 中的使用可显著减少 HTTP 网络流量, 并且可能是提高服务可扩展性和可靠性的重要因素。

2.3.2. Comparison (比较)

有两种实体标签比较函数, 取决于比较上下文是否允许使用弱验证器:

  • 强比较 (Strong comparison): 如果两个实体标签都不是弱验证器且其不透明标签逐字符匹配, 则它们是等效的。
  • 弱比较 (Weak comparison): 如果两个实体标签的不透明标签逐字符匹配, 则它们是等效的, 无论其中一个或两个都被标记为"弱"验证器。

下面的示例显示了一组实体标签对的结果:

ETag 1ETag 2强比较弱比较
W/"1"W/"1"不匹配匹配
W/"1"W/"2"不匹配不匹配
W/"1""1"不匹配匹配
"1""1"匹配匹配

2.4. When to Use Entity-Tags and Last-Modified Dates (何时使用实体标签和最后修改日期)

在对 GET 或 HEAD 的 200 (OK) 响应中, 源服务器:

  • 应该 (SHOULD) 发送实体标签验证器, 除非生成一个不可行。
  • 如果性能考虑支持使用弱实体标签, 或者发送强实体标签不可行, 则可以 (MAY) 发送弱实体标签而不是强实体标签。
  • 如果可行, 应该 (SHOULD) 发送 Last-Modified 值。

换句话说, 源服务器的首选行为是在对检索请求的成功响应中同时发送强实体标签和 Last-Modified 值。

客户端:

  • 如果源服务器提供了实体标签, 则必须 (MUST) 在任何缓存验证请求中发送该实体标签 (使用 If-MatchIf-None-Match)。
  • 如果源服务器仅提供了 Last-Modified 值, 则应该 (SHOULD) 在非子范围缓存验证请求中发送 Last-Modified 值 (使用 If-Modified-Since)。
  • 如果源服务器同时提供了实体标签和 Last-Modified 值, 则应该 (SHOULD) 在缓存验证请求中发送两个验证器。这允许 HTTP/1.0 和 HTTP/1.1 缓存都能适当地响应。