跳到主要内容

8. 表示数据和元数据

8.1. 表示数据

与 HTTP 消息关联的表示数据要么作为消息内容提供, 要么由消息语义和目标 URI 引用. 表示数据的格式和编码由表示元数据头字段定义.

表示数据的数据类型通过 Content-Type 和 Content-Encoding 头字段确定. 这些字段定义了一个两层, 有序的编码模型:

representation-data := Content-Encoding( Content-Type( data ) )

8.2. 表示元数据

表示头字段提供关于表示的元数据. 当消息包含内容时, 表示头字段描述如何解释该数据. 在对 HEAD 请求的响应中, 表示头字段描述如果同一请求是 GET 时本应封装在内容中的表示数据.

8.3. Content-Type

"Content-Type" 头字段指示关联表示的媒体类型: 该表示要么封装在消息内容中, 要么由消息语义确定为选定表示. 所指示的媒体类型定义数据格式, 以及在解码 Content-Encoding 所指示的任何内容编码之后, 接收方应如何在收到消息语义的作用域内处理该数据.

Content-Type = media-type

媒体类型定义于 Section 8.3.1. 字段示例如下:

Content-Type: text/html; charset=ISO-8859-4

生成包含内容的消息的发送方应在该消息中生成 Content-Type 头字段, 除非发送方不知道所封装表示的预期媒体类型. 如果不存在 Content-Type 头字段, 接收方可以假定媒体类型为 "application/octet-stream" ([RFC2046], Section 4.5.1), 或检查数据来确定其类型.

实践中, 资源所有者并不总是正确配置其源服务器来为给定表示提供正确的 Content-Type. 有些用户代理会检查内容并在某些情况下覆盖收到的类型 (例如见 [Sniffing]). 这种 "MIME sniffing" 有得出错误数据结论的风险, 可能使用户暴露于额外安全风险 (例如 "privilege escalation"). 此外, 不同媒体类型经常使用相同数据格式, 只是在该数据的预期处理方式上不同, 而这无法仅通过检查数据区分. 实现 sniffing 时, 鼓励实现者提供让用户禁用它的手段.

虽然 Content-Type 被定义为单例字段, 但它有时会被错误地生成多次, 导致组合字段值看起来像列表. 接收方常尝试使用列表中最后一个语法有效成员来处理此错误, 如果不同实现具有不同错误处理行为, 这可能导致潜在互操作性和安全问题.

8.3.1. 媒体类型

HTTP 在 Content-Type (Section 8.3) 和 Accept (Section 12.5.1) 头字段中使用媒体类型 [RFC2046], 以提供开放且可扩展的数据类型和类型协商. 媒体类型既定义数据格式, 也定义各种处理模型: 如何根据消息上下文处理该数据.

media-type = type "/" subtype parameters
type = token
subtype = token

type/subtype 后可以跟随以分号定界的参数 (Section 5.6.6), 其形式为名称/值对. 参数存在与否可能对媒体类型处理有意义, 具体取决于其在媒体类型注册表中的定义.

匹配 token 产生式的参数值可以不带引号传输. 然而, 带有无效字符 (即不匹配 token 产生式) 的参数值在发送时必须用双引号 (Section 5.6.4) 引起.

parameter      = parameter-name "=" parameter-value
parameter-name = token
parameter-value = ( token / quoted-string )

Note: 有些媒体类型参数接收方以区分大小写的方式处理它们. 通常建议参数名使用小写, 参数值使用最常见的大小写.

媒体类型应按 [BCP13] 中定义的程序向 IANA 注册.

Note: 与其他头字段中的一些类似结构不同, 媒体类型参数不允许在 "=" 字符周围出现空白 (甚至不允许 "bad" whitespace).

8.3.2. Charset

HTTP 在 Content-Type (Section 8.3) 和 Accept-Charset (已弃用; Section 12.5.2) 头字段中使用 charset 名称来指示或协商文本表示的字符编码方案.charset 由不区分大小写的 token 标识.

charset = token

Charset 名称应按照 [RFC2978] Section 2 中定义的程序注册到 IANA "Character Sets" 注册表 (````http://www.iana.org/assignments/character-sets\````).

Note: 媒体类型上的 "charset" 参数可能根据具体媒体类型定义而具有不同语义.

"charset" 参数常与文本内容的媒体类型一起使用. 虽然定义此类媒体类型的标准在很可能提高互操作性时应规定使用某个特定 charset, 但当 charset 影响收到内容的解释时, 强烈鼓励 HTTP 用户和实现者显式指定 charset, 即使媒体类型定义在缺少 charset 参数时默认采用某个 charset (这对 "text" 媒体类型经常如此).

8.3.3. Multipart 类型

MIME 提供若干 "multipart" 类型, 即在单个消息内容中封装一个或多个表示. 所有 multipart 类型共享 [RFC2046] Section 5.1 中定义的通用语法, 并把 boundary 参数作为媒体类型值的一部分. 消息内容本身是协议元素; 发送方必须只生成 CRLF 来表示 body part 之间的换行.

HTTP 消息定帧不使用 multipart boundary 作为 message body 长度指示符, 尽管生成或处理内容的实现可能使用它. 例如, "multipart/form-data" 类型常用于在请求中承载表单数据, 如 [RFC7578] 所述; "multipart/byteranges" 类型由本规范定义, 用于某些 206 (Partial Content) 响应 (见 Section 15.3.7).

8.4. Content-Encoding

"Content-Encoding" 头字段指示除了媒体类型固有编码之外, 已对表示应用了哪些内容编码, 因而也指示为了获得 Content-Type 头字段所引用媒体类型中的数据必须应用哪些解码机制. Content-Encoding 主要用于允许压缩表示数据, 同时不丢失其底层媒体类型的身份.

Content-Encoding = #content-coding

使用示例如下:

Content-Encoding: gzip

如果对表示应用了一个或多个编码, 应用这些编码的发送方必须生成 Content-Encoding 头字段, 按应用顺序列出内容编码. 注意, 名为 "identity" 的编码因其在 Accept-Encoding 中的特殊角色而被保留, 因此不应包含.

其他未由本规范定义的头字段可以提供关于编码参数的附加信息.

与 Transfer-Encoding ([HTTP/1.1] Section 6.1) 不同, Content-Encoding 中列出的编码是表示的特征; 表示以编码形式定义, 关于表示的所有其他元数据都是关于编码形式的, 除非元数据定义另有说明. 通常, 表示只会在渲染或类似使用之前才被解码.

如果媒体类型包含固有编码, 例如始终压缩的数据格式, 那么即使该编码碰巧与某个内容编码使用相同算法, 也不会在 Content-Encoding 中再次声明. 只有在出于某种奇特原因把该内容编码第二次应用以形成表示时, 才会列出此类内容编码. 同样, 源服务器可能选择把同一数据发布为多个表示, 这些表示仅在编码被定义为 Content-Type 的一部分还是 Content-Encoding 的一部分上不同, 因为有些用户代理在处理每个响应时会表现不同 (例如打开 "Save as ..." 对话框, 而不是自动解压并渲染内容).

如果请求消息中的表示具有不可接受的内容编码, 源服务器可以以 415 (Unsupported Media Type) 状态码响应.

8.4.1. 内容编码

内容编码值指示已经或可以应用于表示的编码转换. 内容编码主要用于允许表示被压缩或以其他有用方式转换, 同时不丢失其底层媒体类型身份, 且不丢失信息. 表示常以编码形式存储, 直接传输, 并仅由最终接收方解码.

content-coding   = token

所有内容编码都不区分大小写, 并应注册到 "HTTP Content Coding Registry", 如 Section 16.6.1 所定义. 它们用于 Accept-Encoding (Section 12.5.3) 和 Content-Encoding (Section 8.4) 头字段.

本规范定义以下内容编码:

  • compress (和 x-compress): 见 Section 8.4.1.1.
  • deflate: 见 Section 8.4.1.2.
  • gzip (和 x-gzip): 见 Section 8.4.1.3.

8.4.1.1. Compress 编码

"compress" 编码是一种自适应 Lempel-Ziv-Welch (LZW) 编码 [Welch], 通常由 UNIX 文件压缩程序 "compress" 生成. 接收方应把 "x-compress" 视为等价于 "compress".

8.4.1.2. Deflate 编码

"deflate" 编码是一种 "zlib" 数据格式 [RFC1950], 其中包含使用 Lempel-Ziv (LZ77) 压缩算法和 Huffman coding 组合的 "deflate" 压缩数据流 [RFC1951].

Note: 有些不一致实现会发送没有 zlib wrapper 的 "deflate" 压缩数据.

8.4.1.3. Gzip 编码

"gzip" 编码是一种带 32-bit Cyclic Redundancy Check (CRC) 的 LZ77 编码, 通常由 gzip 文件压缩程序 [RFC1952] 生成. 接收方应把 "x-gzip" 视为等价于 "gzip".

8.5. Content-Language

"Content-Language" 头字段描述表示的预期受众所使用的自然语言. 注意, 这可能不等同于表示内部使用的所有语言.

Content-Language = #language-tag

语言标签定义于 Section 8.5.1. Content-Language 的主要目的是允许用户按自己的首选语言标识和区分表示. 因此, 如果内容仅面向识字的丹麦语受众, 适当字段为

Content-Language: da

如果未指定 Content-Language, 默认含义是内容面向所有语言受众. 这可能表示发送方不认为它特定于任何自然语言, 或者发送方不知道它面向哪种语言.

面向多个受众的内容可以列出多种语言. 例如, 同时以原始 Maori 和英语版本呈现的 "Treaty of Waitangi" 会需要

Content-Language: mi, en

然而, 表示中存在多种语言并不意味着它面向多个语言受众. 例如初学者语言入门材料, 如 "A First Lesson in Latin", 显然意图供识字的英语受众使用. 在这种情况下, Content-Language 适当地只包含 "en".

Content-Language 可以应用于任何媒体类型, 不限于文本文档.

8.5.1. 语言标签

如 [RFC5646] 所定义, language tag 标识人类为向其他人类传达信息而说, 写或以其他方式传递的自然语言. 计算机语言被明确排除.

HTTP 在 Accept-Language 和 Content-Language 头字段中使用语言标签. Accept-Language 使用 Section 12.5.4 中定义的更宽泛 language-range 产生式, 而 Content-Language 使用下文定义的 language-tag 产生式.

language-tag = `<Language-Tag, see [RFC5646], Section 2.1>`

语言标签是一串由一个或多个不区分大小写的 subtag 组成的序列, 每个 subtag 以连字符 ("-", %x2D) 分隔. 在多数情况下, 语言标签由标识相关语言广泛族群的 primary language subtag 组成 (例如 "en" = English), 并可选跟随一系列用于细化或缩小该语言范围的 subtag (例如 "en-CA" = 加拿大传播的英语变体). 语言标签内部不允许空白. 示例标签包括:

fr, en-US, es-419, az-Arab, x-pig-latin, man-Nkoo-GN

更多信息见 [RFC5646].

8.6. Content-Length

"Content-Length" 头字段以十进制非负整数八位字节数指示关联表示的数据长度. 当把表示作为内容传输时, Content-Length 专门指封装的数据量, 以便可用于定界定帧 (例如 [HTTP/1.1] Section 6.2). 在其他预期完整表示的情况下, Content-Length 指表示当前选定长度.

Content-Length = 1*DIGIT

示例为

Content-Length: 3495

当方法为封装内容定义了含义且未发送 Transfer-Encoding 时, 用户代理应在请求中发送 Content-Length. 例如, POST 请求通常会发送 Content-Length 头字段, 即使值为 0 (表示空内容).

当请求消息不包含内容且方法语义不预期此类数据时, 用户代理不应发送 Content-Length 头字段.

服务器可以在对 HEAD 请求 (Section 9.3.2) 的响应中发送 Content-Length 头字段; 除非其字段值等于如果同一请求使用 GET 方法时响应内容中本应发送的十进制八位字节数, 否则服务器不得在此类响应中发送 Content-Length.

服务器可以在对条件 GET 请求的 304 (Not Modified) 响应 (Section 15.4.5) 中发送 Content-Length 头字段; 除非其字段值等于对同一请求的 200 (OK) 响应内容中本应发送的十进制八位字节数, 否则服务器不得在此类响应中发送 Content-Length.

服务器不得在任何状态码为 1xx (Informational) 或 204 (No Content) 的响应中发送 Content-Length 头字段. 服务器不得在对 CONNECT 请求 (Section 9.3.6) 的任何 2xx (Successful) 响应中发送 Content-Length 头字段.

除上述定义的情况外, 在没有 Transfer-Encoding 时, 如果在发送完整头部区段之前已知内容大小, 源服务器应发送 Content-Length 头字段. 这将允许下游接收方衡量传输进度, 知道自身定帧边界, 并为后续请求复用连接.

由于 Content-Length 在 HTTP/1.1 中用于消息定界, 即使消息定帧不受 HTTP/1.1 规则约束, 其字段值也会影响接收方如何解析消息. 如果该值与表示的实际数据长度不匹配, 结果可能从错误消息定帧到潜在请求走私或响应拆分不等, 具体取决于环境.

发送方不得在任何包含 Transfer-Encoding 头字段的消息中发送 Content-Length 头字段.

Note: HTTP 使用 Content-Length 进行消息定帧, 与 MIME 中同一字段的使用有显著差异; 在 MIME 中, 它是仅用于 "message/external-body" 媒体类型内的可选字段.

8.7. Content-Location

"Content-Location" 头字段引用一个 URI, 该 URI 可用作与此消息内容中的表示对应的特定资源的标识符. 换言之, 如果在此消息生成时对该 URI 执行 GET 请求, 那么 200 (OK) 响应将包含与此消息中作为内容封装的表示相同的表示.

Content-Location = absolute-URI / partial-URI

Content-Location 值不是目标 URI (Section 7.1) 的替代. 它是表示元数据. 它与 [RFC2557] Section 4 中为 MIME body parts 定义的同名头字段具有相同语法和语义. 然而, 它出现在 HTTP 消息中时, 对 HTTP 接收方具有一些特殊含义.

如果 Content-Location 包含在 2xx (Successful) 响应消息中, 且其值引用的 URI 与目标 URI 具有相同 scheme, authority 和 path, 则接收方可以认为内容是该目标资源的当前表示. 对于 GET (Section 9.3.1) 或 HEAD (Section 9.3.2) 请求, 这与服务器未提供 Content-Location 时的默认语义相同. 对于 PUT (Section 9.3.4) 或 POST (Section 9.3.3) 这样的状态变更请求, 它意味着服务器响应内容包含该目标资源的当前表示, 从而将其与可能只报告动作结果的表示区分开 (例如 "It worked!"). 这允许创作应用更新其本地副本, 而无需后续 GET 请求.

如果 Content-Location 包含在 2xx (Successful) 响应消息中, 且其字段值引用不同于目标 URI 的 URI, 则源服务器声称该 URI 是对应所封装表示的另一个资源的标识符. 只有当两个标识符共享同一资源所有者时, 这种声明才可信, 而这无法通过 HTTP 以程序化方式确定.

  • 对 GET 或 HEAD 请求的响应而言, 这表示目标 URI 引用受内容协商约束的资源, 而 Content-Location 字段值是选定表示的更具体标识符.

  • 对 POST 请求的 201 (Created) 响应而言, Content-Location 字段值是对包含新资源对应当前表示的资源的引用.

否则, 这样的 Content-Location 表示此内容是报告所请求动作状态的表示, 且同一报告可在给定 URI 处 (未来使用 GET) 获得. 例如, 通过 POST 请求进行的购买事务可能把收据文档作为 200 (OK) 响应的内容; Content-Location 字段值提供了未来检索同一收据副本的标识符.

在请求消息中发送 Content-Location 的用户代理声明其值引用用户代理最初获得所封装表示内容的位置 (在该用户代理进行任何修改之前). 换言之, 用户代理正在提供指向原始表示来源的反向链接.

源服务器收到请求消息中的 Content-Location 字段时, 必须把该信息视为临时请求上下文, 而不是要随表示保存的元数据. 源服务器可以使用该上下文指导请求处理, 或把它保存作其他用途, 例如源链接或版本元数据. 然而, 源服务器不得使用此类上下文信息改变请求语义.

例如, 如果客户端对协商资源发出 PUT 请求且源服务器接受该 PUT (未重定向), 则该资源的新状态预期与该 PUT 中提供的一个表示一致; Content-Location 不能作为反向内容选择标识符, 只更新协商表示之一. 如果用户代理想要后一种语义, 它本应直接对 Content-Location URI 应用 PUT.

8.8. 验证器字段

如果资源元数据可在前置条件 (Section 13) 中用于发出条件请求 (Section 13.1), 则称为 "验证器" (validator).

验证器字段传达选定表示 (Section 3.2) 的当前验证器.

在对安全请求的响应中, 验证器字段描述选定表示 (Section 3.2). 注意, 根据状态码语义, 给定响应的选定表示不一定与作为响应内容封装的表示相同.

在对状态变更请求的成功响应中, 验证器字段描述由于处理该请求而替换先前选定表示的新表示.

例如, 201 (Created) 响应中的 ETag 头字段传达由请求创建资源的验证器, 而对 PUT 的 200 (OK) 响应中的 ETag 头字段传达由于 PUT 请求而替换先前选定表示的新表示的验证器.

本规范定义两种常用于观察资源状态并测试请求前置条件的元数据形式: 修改日期 (Section 8.8.2) 和不透明实体标签 (Section 8.8.3). 关于各种设计考虑的附加信息见 Section 13.2.

8.8.1. 弱与强

验证器有两种类型: 强和弱. 弱验证器容易生成, 但用于比较时有用性低得多. 强验证器非常适合比较, 但可能很难 (有时不可能) 高效生成. HTTP 不要求所有形式资源都遵循同等强度的验证器, 而是暴露正在使用的验证器类型, 并限制弱验证器何时可作为前置条件使用.

"强验证器" (strong validator) 是一种表示元数据, 只要表示数据发生可在对 GET 的 200 (OK) 响应内容中观察到的变化, 它的值就会改变.

强验证器可能因表示数据变化以外的原因改变, 例如表示元数据中具有语义意义的部分发生变化 (例如 Content-Type), 但只在需要使远程缓存和创作工具保存的响应失效时才改变值, 最符合源服务器利益.

缓存条目可能不受过期时间影响而保持任意长时间. 因此, 缓存可能尝试使用很久以前获得的验证器来验证条目. 强验证器在与特定资源随时间关联的所有表示的所有版本之间是唯一的. 然而, 这并不意味着它在不同资源的表示之间也唯一 (即同一强验证器可能同时用于多个资源的表示, 且不意味着这些表示等价).

实践中使用多种强验证器. 最佳验证器基于严格修订控制, 其中表示的每次更改都会在该表示可供 GET 访问之前分配唯一节点名称和修订标识符. 如果数据在发送响应头部区段之前可用, 且每次收到验证请求时无需重新计算摘要, 则对表示数据应用抗碰撞哈希函数也足够. 然而, 如果资源具有仅元数据不同的不同表示, 例如可能发生在媒体类型内容协商而这些媒体类型恰好共享相同数据格式时, 则源服务器需要在验证器中加入附加信息以区分这些表示.

相比之下, "弱验证器" (weak validator) 是一种表示元数据, 它可能不会在表示数据每次变化时都改变. 这种弱性可能源于值计算方式的限制 (例如时钟分辨率), 无法确保资源所有可能表示的唯一性, 或希望按某个自定等价集合而不是唯一数据序列对表示分组.

每当源服务器认为先前表示不可接受作为当前表示的替代时, 源服务器应改变弱实体标签. 换言之, 每当源服务器希望缓存使旧响应失效时, 弱实体标签都应改变.

例如, 天气报告的表示基于动态测量每秒改变内容, 可以从源服务器角度把等价表示分组为具有相同弱验证器的集合, 以允许缓存表示在合理时间内有效 (可能根据服务器负载或天气质量动态调整). 同样, 如果表示的修改时间仅以一秒分辨率定义, 且表示可能在同一秒内被修改两次并在两次修改之间被检索, 则该修改时间可能是弱验证器.

同样, 如果某个验证器同时被给定资源的两个或多个表示共享, 且这些表示不具有相同表示数据, 则该验证器是弱的. 例如, 如果源服务器为应用 gzip 内容编码的表示和未应用内容编码的表示发送同一验证器, 则该验证器是弱的. 然而, 两个同时存在的表示如果仅表示元数据不同, 例如同一表示数据可用两种不同媒体类型提供, 则可以共享同一强验证器.

强验证器可用于所有条件请求, 包括缓存验证, 部分内容范围和避免 "lost update".弱验证器仅在客户端不要求与先前获得的表示数据完全相等时可用, 例如验证缓存条目或把 Web 遍历限制于最近变化.

8.8.2. Last-Modified

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

Last-Modified = HTTP-date

使用示例如下:

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

8.8.2.1. 生成

对于能够合理且一致确定最后修改日期的任何选定表示, 源服务器应发送 Last-Modified, 因为它在条件请求和评估缓存新鲜度 ([CACHING]) 中的使用可以大幅减少不必要传输, 并显著提高服务可用性和可扩展性.

表示通常是资源接口背后许多部分的总和.last-modified 时间通常是这些部分中任一部分最近一次改变的时间. 如何为任意给定资源确定该值是超出本规范范围的实现细节. 对 HTTP 而言重要的是 Last-Modified 头字段的接收方如何使用其值发起条件请求并测试本地缓存响应的有效性.

源服务器应尽可能在接近为其响应生成 Date 字段值的时间获得表示的 Last-Modified 值. 这允许接收方准确评估表示的修改时间, 尤其是在表示接近响应生成时间发生变化时.

具有时钟 (如 Section 5.6.7 所定义) 的源服务器不得发送晚于服务器消息发起时间 (Date) 的 Last-Modified 日期. 如果最后修改时间来自实现特定元数据, 且按源服务器时钟评估为未来某个时间, 则源服务器必须用消息发起日期替换该值. 这防止未来修改日期对缓存验证造成不利影响.

没有时钟的源服务器不得为响应分配 Last-Modified 值, 除非这些值由其他具有可靠时钟的系统或用户与资源关联.

8.8.2.2. 比较

Last-Modified 时间在请求中用作验证器时隐式为弱, 除非可以使用以下规则推断它是强的:

  • 该验证器正由源服务器与表示的实际当前验证器比较, 并且

  • 该源服务器可靠地知道相关表示在所呈现验证器覆盖的那一秒内没有改变两次.

  • 该验证器即将由客户端在 If-Modified-Since, If-Unmodified-Since 或 If-Range 头字段中使用, 因为客户端拥有相关表示的缓存条目, 并且

  • 该缓存条目包含 Date 值, 给出源服务器发送原始响应的时间, 并且

  • 所呈现 Last-Modified 时间至少早于 Date 值 60 秒.

  • 该验证器正由中间缓存与其缓存条目中为该表示存储的验证器比较, 并且

  • 该缓存条目包含 Date 值, 给出源服务器发送原始响应的时间, 并且

  • 所呈现 Last-Modified 时间至少早于 Date 值 60 秒.

此方法依赖这样一个事实: 如果源服务器在同一秒内发送了两个不同响应, 但二者具有相同 Last-Modified 时间, 则其中至少一个响应会具有等于其 Last-Modified 时间的 Date 值. 任意的 60 秒限制用于防范 Date 和 Last-Modified 值由不同时钟生成或在响应准备期间略有不同时间生成的可能性. 如果认为 60 秒太短, 实现可以使用大于 60 秒的值.

8.8.3. ETag

响应中的 "ETag" 头字段提供选定表示的当前实体标签, 该标签在请求处理结束时确定. 实体标签是一种不透明验证器, 用于区分同一资源的多个表示, 无论这些多个表示是由于资源状态随时间变化, 内容协商导致多个表示同时有效, 还是二者兼有. 实体标签由不透明 quoted string 组成, 可能以弱性指示符为前缀.

ETag       = entity-tag

entity-tag = [ weak ] opaque-tag
weak = %s"W/"
opaque-tag = DQUOTE *etagc DQUOTE
etagc = %x21 / %x23-7E / obs-text
; VCHAR except double quotes, plus obs-text

Note: 先前 opaque-tag 被定义为 quoted-string ([RFC2616], Section 3.11); 因此, 有些接收方可能执行反斜杠反转义. 所以服务器应避免在实体标签中使用反斜杠字符.

实体标签可能比修改日期更可靠, 原因有很多: 服务器可能没有可用时钟; 出于访问控制目的, 资源修改时间可能被设置到未来; 修改时间的分辨率受其存储方式限制; 分布式系统可能难以同步时钟; 实体标签可以包含附加元数据, 如 Content-Encoding 头字段值, 以更好地区分表示; 等等.

如果两个实体标签的 opaque-tags 逐字符匹配, 则二者等价, 无论其中一个或两个是否被标记为弱.

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

ETag: W/"xyzzy"
ETag: ""

8.8.3.1. 生成

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

例如, 对所有变更都应用实现特定版本控制的资源, 可能使用内部修订号, 或许再结合内容协商的变体标识符, 以准确区分表示. 其他实现可能使用表示内容的抗碰撞哈希, 多个文件属性的组合, 或具有亚秒分辨率的修改时间戳.

对于能够合理且一致确定变化检测的任何选定表示, 源服务器应发送 ETag, 因为实体标签在条件请求和评估缓存新鲜度 ([CACHING]) 中的使用可以显著减少 HTTP 网络流量, 并显著促进服务可扩展性和可靠性.

8.8.3.2. 比较

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

  • 强比较: 如果两个实体标签都不是弱的, 且它们的 opaque-tags 逐字符匹配, 则二者等价.

  • 弱比较: 如果两个实体标签的 opaque-tags 逐字符匹配, 则二者等价, 无论其中一个或两个是否被标记为弱.

下表示例展示了一组实体标签对以及弱比较和强比较函数的结果:

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

8.8.3.3. 示例: 随内容协商资源变化的实体标签

考虑一个受内容协商 (Section 12.1) 约束的资源, 其响应 GET 请求时发送的表示会根据 Accept-Encoding 请求头字段 (Section 12.5.3) 变化:

>> Request:

GET /index HTTP/1.1
Host: www.example.com
Accept-Encoding: gzip

>> Response:

HTTP/1.1 200 OK
Date: Fri, 26 Mar 2010 00:05:00 GMT
ETag: "123-a"
Content-Length: 70
Vary: Accept-Encoding
Content-Type: text/plain
Content-Encoding: gzip

[...]

对于另一组请求头字段, 服务器可能发送不同表示:

>> Request:

GET /index HTTP/1.1
Host: www.example.com

>> Response:

HTTP/1.1 200 OK
Date: Fri, 26 Mar 2010 00:05:00 GMT
ETag: "123-b"
Content-Length: 400
Vary: Accept-Encoding
Content-Type: text/plain

[...]

这两个响应中的实体标签差异表示所发送的表示包含不同数据. 本例中使用的强实体标签允许同时为缓存验证和范围请求使用条件请求.

相比之下, 如果源服务器即时生成 gzip 编码表示, 而不检查此前是否已经生成过任何版本的表示, 则它可能使用弱实体标签来指示等价内容:

>> Response:

HTTP/1.1 200 OK
Date: Fri, 26 Mar 2010 00:05:00 GMT
ETag: W/"123"
Content-Length: 70
Vary: Accept-Encoding
Content-Type: text/plain
Content-Encoding: gzip

[...]

在这种情况下, 服务器假定表示的压缩版本与未压缩版本等价, 同时又没有足够好地跟踪版本以确定强验证器.