Skip to main content

8. Representation Data and Metadata (表示数据和元数据)

8.1. Representation Data (表示数据)

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

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

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

8.2. Representation Metadata (表示元数据)

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

8.3. Content-Type

"Content-Type"头部字段指示关联表示的媒体类型: 消息内容中包含的表示或所选表示,由消息语义确定. 指示的媒体类型定义了数据格式以及接收者在接收到的消息语义范围内,在Content-Encoding指示的任何内容编码被解码后,如何处理该数据.

Content-Type = media-type

媒体类型在第8.3.1节中定义. 字段的示例是

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

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

实际上,资源所有者并不总是正确配置其源服务器以为给定表示提供正确的Content-Type. 一些用户代理检查内容,并在某些情况下覆盖接收到的类型(例如,参见[Sniffing]). 这种"MIME嗅探"有可能对数据得出不正确的结论,这可能使用户面临额外的安全风险(例如,"权限提升"). 此外,不同的媒体类型通常共享通用数据格式,仅在数据的预期处理方式上有所不同,这是无法通过单独检查数据来区分的. 当实现嗅探时,鼓励实现者提供一种方法让用户禁用它.

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

8.3.1. Media Type (媒体类型)

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

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

type和subtype令牌不区分大小写.

type/subtype可以 (MAY) 后跟分号分隔的参数(第5.6.6节),采用名称/值对的形式. 参数的存在或不存在可能对媒体类型的处理很重要,具体取决于其在媒体类型注册表中的定义. 参数值可能区分大小写也可能不区分大小写,具体取决于参数名称的语义.

例如,以下媒体类型在描述以UTF-8字符编码方案编码的HTML文本数据方面是等效的,但首选第一个以保持一致性("charset"参数值在[RFC2046]的第4.1.2节中定义为不区分大小写):

text/html;charset=utf-8
Text/HTML;Charset="utf-8"
text/html; charset="utf-8"
text/html;charset=UTF-8

媒体类型应该 (ought to) 根据[BCP13]中定义的过程向IANA注册.

8.3.2. Charset (字符集)

HTTP使用"charset"名称来指示或协商文本表示的字符编码方案([RFC6365],第2节). 在本文档定义的字段中,charset名称要么出现在参数中(Content-Type),要么对于Accept-Encoding,以纯令牌的形式出现. 在这两种情况下,charset名称都不区分大小写地匹配.

Charset名称应该 (ought to) 根据[RFC2978]第2节中定义的过程在IANA"字符集"注册表(https://www.iana.org/assignments/character-sets)中注册.

注意: 理论上,charset名称由[RFC2978]第2.3节中定义的"mime-charset" ABNF规则定义(如[Err1912]中更正的). 该规则允许两个不包含在"token"中的字符(""),但在撰写本文时注册的charset名称都不包含大括号(参见[Err5433]).

8.3.3. Multipart Types (多部分类型)

MIME提供了许多"多部分"类型——在单个消息主体中封装一个或多个表示. 所有多部分类型共享通用语法,如[RFC2046]的第5.1.1节中定义,并包括边界参数作为媒体类型值的一部分. 消息主体本身是一个协议元素; 发送者必须 (MUST) 仅生成CRLF来表示主体部分之间的换行符.

HTTP消息帧不使用多部分边界作为消息主体长度的指示符,尽管生成或处理内容的实现可能会使用它. 例如,"multipart/form-data"类型通常用于在请求中携带表单数据,如[RFC7578]中所述,并且"multipart/byteranges"类型由本规范定义用于某些206(Partial Content)响应(参见第15.3.7节).

8.4. Content-Encoding

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

Content-Encoding = #content-coding

其使用示例是

Content-Encoding: gzip

如果已将一个或多个编码应用于表示,则应用编码的发送者必须 (MUST) 生成Content-Encoding头部字段,该字段按应用顺序列出内容编码. 请注意,名为"identity"的编码因其在Accept-Encoding中的特殊作用而保留,因此不应该 (SHOULD NOT) 包含.

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

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

如果媒体类型包括固有编码,例如始终压缩的数据格式,则即使它恰好与内容编码之一相同的算法,也不会在Content-Encoding中重述该编码. 仅当出于某种奇怪的原因第二次应用它以形成表示时,才会列出这样的内容编码. 同样,源服务器可能选择将相同的数据发布为多个表示,这些表示仅在编码是定义为Content-Type的一部分还是Content-Encoding的一部分上有所不同,因为某些用户代理在处理每个响应时会有不同的行为(例如,打开"另存为..."对话框而不是自动解压缩和渲染内容).

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

8.4.1. Content Codings (内容编码)

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

content-coding   = token

所有内容编码都不区分大小写,并且应该 (ought to) 在"HTTP内容编码注册表"中注册,如第16.6节所述.

内容编码值用于Accept-Encoding(第12.5.3节)和Content-Encoding(第8.4节)头部字段.

8.4.1.1. Compress Coding (压缩编码)

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

8.4.1.2. Deflate Coding (deflate编码)

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

注意: 一些不符合规范的实现在没有zlib包装器的情况下发送"deflate"压缩数据.

8.4.1.3. Gzip Coding (gzip编码)

"gzip"编码是一种带有32位循环冗余检查(CRC)的LZ77编码,通常由gzip文件压缩程序[RFC1952]生成. 接收者应该 (SHOULD) 将"x-gzip"视为等同于"gzip".

8.5. Content-Language

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

Content-Language = #language-tag

语言标签在第8.5.1节中定义. Content-Language的主要目的是允许用户根据用户自己的首选语言识别和区分表示. 因此,如果内容仅针对懂丹麦语的受众,则适当的字段是

Content-Language: da

如果未指定Content-Language,则默认内容适用于所有语言受众. 这可能意味着发送者不认为它特定于任何自然语言,或者发送者不知道它针对哪种语言.

可以 (MAY) 为针对多个受众的内容列出多种语言. 例如,同时以原始毛利语和英语版本呈现的"怀唐伊条约"的演绎将要求

Content-Language: mi, en

然而,仅仅因为表示中存在多种语言并不意味着它针对多个语言受众. 一个例子是初学者语言入门,例如"拉丁语第一课",它显然旨在供懂英语的受众使用. 在这种情况下,Content-Language应该只包括"en".

Content-Language可以 (MAY) 应用于任何媒体类型——它不限于文本文档.

8.5.1. Language Tags (语言标签)

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

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

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

语言标签是一个或多个不区分大小写的子标签的序列,每个子标签由连字符("-", %x2D)分隔. 在大多数情况下,语言标签由标识广泛相关语言家族的主要语言子标签(例如,"en" = 英语)组成,可选地后跟一系列细化或缩小该语言范围的子标签(例如,"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]的第6.2节). 在其他情况下,Content-Length指示所选表示的当前长度,接收者可以使用它来估计传输时间或与先前存储的表示进行比较.

Content-Length = 1*DIGIT

一个示例是

Content-Length: 3495

当方法为封闭内容定义含义且未发送Transfer-Encoding时,用户代理应该 (SHOULD) 在请求中发送Content-Length. 例如,用户代理通常在POST请求中发送Content-Length,即使值为0(指示空内容). 当请求消息不包含内容且方法语义不期望此类数据时,用户代理不应该 (SHOULD NOT) 发送Content-Length头部字段.

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

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

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

除了上面定义的情况外,在没有Transfer-Encoding的情况下,当在发送完整头部区段之前已知内容大小时,源服务器应该 (SHOULD) 发送Content-Length头部字段. 这将允许下游接收者测量传输进度,知道何时接收到完整消息,并可能重用连接以进行其他请求.

任何大于或等于零的Content-Length字段值都是有效的. 由于内容长度没有预定义的限制,接收者必须 (MUST) 预期可能很大的十进制数字,并防止由于整数转换溢出或整数转换导致的精度损失而导致的解析错误(第17.5节).

由于Content-Length在HTTP/1.1中用于消息分隔,因此即使直接连接不使用HTTP/1.1,其字段值也会影响下游接收者解析消息的方式. 如果消息由下游中间人转发,与接收到的消息帧不一致的Content-Length字段值可能会由于请求走私或响应拆分而导致安全故障.

因此,发送者不得 (MUST NOT) 转发已知不正确的Content-Length头部字段值的消息.

同样,发送者不得 (MUST NOT) 转发Content-Length头部字段值与上述ABNF不匹配的消息,但有一个例外: 接收到由相同十进制值重复作为逗号分隔列表组成的Content-Length头部字段值的接收者(例如,"Content-Length: 42, 42")可以 (MAY) 拒绝消息为无效或用十进制值的单个实例替换该无效字段值,因为这可能表明上游消息处理器生成或组合了重复项.

8.7. Content-Location

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

Content-Location = absolute-URI / partial-URI

字段值是absolute-URI或partial-URI. 在后一种情况下(第4节),引用的URI相对于目标URI([URI],第5节).

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

如果Content-Location包含在2xx(Successful)响应消息中,并且其值引用(转换为绝对形式后)与目标URI相同的URI,则接收者可以 (MAY) 将内容视为该资源在消息发起日期指示的时间的当前表示. 对于GET(第9.3.1节)或HEAD(第9.3.2节)请求,这与服务器未提供Content-Location时的默认语义相同. 对于像PUT(第9.3.4节)或POST(第9.3.3节)这样的状态更改请求,它意味着服务器的响应包含该资源的新表示,从而将其与可能仅报告操作的表示区分开来(例如,"成功了!"). 这允许创作应用程序更新其本地副本,而无需后续GET请求.

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

  • 对于对GET或HEAD请求的响应,这表明目标URI引用受内容协商约束的资源,并且Content-Location字段值是所选表示的更具体标识符.
  • 对于对状态更改方法的201(Created)响应,与Location字段值相同的Content-Location字段值表示此内容是新创建资源的当前表示.
  • 否则,这样的Content-Location表示此内容是报告所请求操作状态的表示,并且相同的报告可用(用于将来使用GET访问)在给定的URI. 例如,通过POST请求进行的购买交易可能包括收据文档作为200(OK)响应的内容; Content-Location字段值提供了用于将来检索该相同收据副本的标识符.

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

接收到请求消息中的Content-Location字段的源服务器必须 (MUST) 将该信息视为瞬态请求上下文,而不是作为要逐字保存为表示一部分的元数据. 源服务器可以 (MAY) 使用该上下文来指导处理请求或将其保存用于其他用途,例如在源链接或版本控制元数据中. 然而,源服务器不得 (MUST NOT) 使用此类上下文信息来更改请求语义.

例如,如果客户端对协商资源进行PUT请求,并且源服务器接受该PUT(无重定向),则该资源的新状态应该与该PUT中提供的一个表示一致; Content-Location不能用作反向内容选择标识符的形式来仅更新协商表示之一. 如果用户代理想要后者的语义,它将直接将PUT应用于Content-Location URI.

8.8. Validator Fields (验证器字段)

如果资源元数据可以在前提条件(第13.1节)中使用以进行条件请求(第13节),则将其称为"验证器" (validator). 验证器字段传达所选表示(第3.2节)的当前验证器.

在对安全请求的响应中,验证器字段描述源服务器在处理响应时选择的所选表示. 请注意,根据方法和状态码语义,给定响应的所选表示不一定与作为响应内容包含的表示相同.

在对状态更改请求的成功响应中,验证器字段描述作为处理请求的结果已替换先前所选表示的新表示.

例如,201(Created)响应中的ETag字段传达新创建资源表示的实体标签,以便实体标签可以在以后的条件请求中用作验证器以防止"丢失更新"问题.

本规范定义了两种通常用于观察资源状态和测试前提条件的元数据形式: 修改日期(第8.8.2节)和不透明实体标签(第8.8.3节). 反映资源状态的附加元数据已由HTTP的各种扩展定义,例如Web分布式创作和版本控制[WEBDAV],这超出了本规范的范围.

8.8.1. Weak versus Strong (弱与强)

验证器有两种类型: 强或弱. 弱验证器易于生成但对比较的用处要小得多. 强验证器对于比较是理想的,但可能非常难以(有时甚至不可能)有效生成. HTTP不是强制所有形式的资源遵守相同强度的验证器,而是公开使用中的验证器类型,并对何时可以使用弱验证器作为前提条件施加限制.

"强验证器" (strong validator) 是表示元数据,每当发生对表示数据的更改时,该元数据的值就会更改,该更改在对GET的200(OK)响应的内容中是可观察到的.

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

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

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

相反,"弱验证器" (weak validator) 是表示元数据,它可能不会针对表示数据的每次更改而更改. 这种弱点可能是由于如何计算值的限制(例如,时钟分辨率),无法确保资源的所有可能表示的唯一性,或者资源所有者希望通过某些自我确定的等价集而不是唯一的数据序列对表示进行分组.

每当源服务器认为先前的表示不可接受作为当前表示的替代品时,它应该 (SHOULD) 更改弱实体标签. 换句话说,每当源服务器希望缓存使旧响应无效时,弱实体标签就应该更改.

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

同样,如果验证器在同一时间由给定资源的两个或多个表示共享,则验证器是弱的,除非这些表示具有相同的表示数据. 例如,如果源服务器为应用了gzip内容编码的表示发送与没有内容编码的表示相同的验证器,则该验证器是弱的. 然而,如果两个同时表示仅在表示元数据上不同,例如当相同表示数据可用两种不同的媒体类型时,它们可能共享相同的强验证器.

强验证器可用于所有条件请求,包括缓存验证,部分内容范围和"丢失更新"避免. 弱验证器仅在客户端不需要与先前获得的表示数据完全相等时才可用,例如在验证缓存条目或将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. Generation (生成)

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

表示通常是资源接口后面许多部分的总和. 最后修改时间通常是这些部分中任何一个被更改的最近时间. 如何为任何给定资源确定该值是超出本规范范围的实现细节.

源服务器应该 (SHOULD) 尽可能接近为其响应生成Date字段值的时间获得表示的Last-Modified值. 这允许接收者对表示的修改时间进行准确评估,特别是如果表示在生成响应的时间附近更改.

具有时钟(如第5.6.7节中定义)的源服务器不得 (MUST NOT) 生成晚于服务器消息发起时间(Date,第6.6.1节)的Last-Modified日期. 如果最后修改时间是从评估为将来某个时间(根据源服务器的时钟)的特定于实现的元数据派生的,则源服务器必须 (MUST) 用消息发起日期替换该值. 这可以防止未来的修改日期对缓存验证产生不利影响.

没有时钟的源服务器不得 (MUST NOT) 为响应生成Last-Modified日期,除非该日期值由某个其他系统(大概是具有时钟的系统)分配给资源.

8.8.2.2. Comparison (比较)

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

  • 验证器由源服务器与表示的实际当前验证器进行比较,并且
  • 该源服务器可靠地知道关联的表示在所呈现的验证器覆盖的秒内没有更改两次;

  • 验证器即将由客户端在If-Modified-Since,If-Unmodified-Since或If-Range头部字段中使用,因为客户端具有关联表示的缓存条目,并且
  • 该缓存条目包括一个Date值,该值至少比Last-Modified值晚一秒,并且客户端有理由相信它们是由同一时钟生成的,或者Last-Modified和Date值之间有足够的差异,使时钟同步问题不太可能;

  • 验证器由中间缓存与其表示的缓存条目中存储的验证器进行比较,并且
  • 该缓存条目包括一个Date值,该值至少比Last-Modified值晚一秒,并且缓存有理由相信它们是由同一时钟生成的,或者Last-Modified和Date值之间有足够的差异,使时钟同步问题不太可能.

此方法依赖于以下事实: 如果源服务器在同一秒内发送了两个不同的响应,但两者都具有相同的Last-Modified时间,则这些响应中的至少一个将具有等于其Last-Modified时间的Date值.

8.8.3. ETag

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

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

注意: 以前,opaque-tag被定义为quoted-string([RFC2616],第3.11节); 因此,一些接收者可能会执行反斜杠转义. 因此,服务器应该避免在实体标签中使用反斜杠字符.

在不方便存储修改日期,HTTP-date值的一秒分辨率不够,或者未一致维护修改日期的情况下,实体标签对于验证可能比修改日期更可靠.

示例:

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

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

发送者可以 (MAY) 在尾部区段中发送ETag字段(参见第6.5节). 然而,由于尾部经常被忽略,因此最好将ETag作为头部字段发送,除非实体标签是在发送内容时生成的.

8.8.3.1. Generation (生成)

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

例如,对所有更改应用特定于实现的版本控制的资源可能使用内部修订号(可能与内容协商的变体标识符结合)来准确区分表示. 其他实现可能使用表示内容的抗冲突哈希,各种文件属性的组合或具有亚秒分辨率的修改时间戳.

对于可以合理且一致地确定更改检测的任何所选表示,源服务器应该 (SHOULD) 发送ETag,因为实体标签在条件请求和评估缓存新鲜度([CACHING])中的使用可以大大减少不必要的传输并显著提高服务可用性,可扩展性和可靠性.

8.8.3.2. Comparison (比较)

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

强比较 (Strong comparison): 如果两者都不弱并且它们的opaque-tags逐字符匹配,则两个实体标签是等效的.

弱比较 (Weak comparison): 如果它们的opaque-tags逐字符匹配,则两个实体标签是等效的,无论其中一个或两者都被标记为"弱".

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

ETag 1ETag 2Strong ComparisonWeak Comparison
W/"1"W/"1"no matchmatch
W/"1"W/"2"no matchno match
W/"1""1"no matchmatch
"1""1"matchmatch

8.8.3.3. Example: Entity Tags Varying on Content-Negotiated Resources (示例: 内容协商资源上变化的实体标签)

考虑受内容协商约束的资源(第12节),其中响应GET请求发送的表示基于Accept-Encoding请求头部字段(第12.5.3节)而变化:

>> Request:

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

在这种情况下,响应可能使用也可能不使用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

Hello World!
Hello World!
Hello World!
Hello World!
Hello World!

确实使用gzip内容编码的替代表示将是:

>> Response:

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

...binary data...

注意: 内容编码是表示数据的属性,因此内容编码表示的强实体标签必须与未编码表示的实体标签不同,以防止在缓存更新和范围请求期间的潜在冲突. 相反,传输编码([HTTP/1.1]的第7节)仅在消息传输期间应用,不会导致不同的实体标签.