3. Representations (表示)
考虑到资源可以是任何事物,而HTTP提供的统一接口类似于一个窗口,通过该窗口只能通过与另一侧的某个独立参与者通信来观察和作用于这样的事物,因此需要一个抽象来"表示"("代替")该事物在通信中的当前或期望状态。这个抽象称为表示 (representation) [REST]。
就HTTP而言,"表示" (representation) 是指旨在反映给定资源的过去、当前或期望状态的信息,以一种可以通过协议轻松传达的格式,由一组表示元数据和可能无限的表示数据流组成。
源服务器 (origin server) 可能被提供或能够生成多个表示,每个表示都旨在反映目标资源的当前状态。在这种情况下,源服务器使用某种算法从这些表示中选择一个作为对给定请求最适用的表示,通常基于内容协商 (content negotiation)。这个"选定的表示" (selected representation) 用于提供数据和元数据,以评估条件请求 [RFC7232] 并构造对GET [Section 4.3.1] 的200 (OK) 和304 (Not Modified) 响应的有效载荷。
3.1. Representation Metadata (表示元数据)
表示头字段 (representation header fields) 提供有关表示的元数据。当消息包含有效载荷主体 (payload body) 时,表示头字段描述如何解释封装在有效载荷主体中的表示数据。在对HEAD请求的响应中,表示头字段描述如果相同的请求是GET请求,则会封装在有效载荷主体中的表示数据。
3.1.1. Processing Representation Data (处理表示数据)
表示元数据的目的是允许接收方知道他们正在处理什么格式,表示使用什么语言或语言,以及中间介体对表示数据应用了哪些转换。
3.1.1.1. Media Type (媒体类型)
HTTP在 Content-Type (Section 3.1.1.5) 和 Accept (Section 5.3.2) 头字段中使用互联网媒体类型 [RFC2046],以提供开放和可扩展的数据类型化和类型协商。媒体类型定义了数据格式和各种处理模型:如何根据接收到的每个上下文处理该数据。
media-type = type "/" subtype *( OWS ";" OWS parameter )
type = token
subtype = token
类型/子类型后面可以跟随名称=值对形式的参数。
parameter = token "=" ( token / quoted-string )
类型、子类型和参数名称标记不区分大小写。参数值可能区分大小写,也可能不区分大小写,具体取决于参数名称的语义。参数的存在或缺失可能对媒体类型的处理很重要,具体取决于其在媒体类型注册表中的定义。
与令牌生成规则匹配的参数值可以作为令牌或在带引号的字符串中传输。带引号和不带引号的值是等效的。例如,以下示例都是等效的,但为了保持一致性,首选第一个:
text/html;charset=utf-8
text/html;charset=UTF-8
text/HTML;charset="utf-8"
text/html; charset="utf-8"
互联网媒体类型应根据 [BCP13] 中定义的程序在IANA注册。
3.1.1.2. Charset (字符集)
HTTP使用字符集名称来指示或协商文本表示的字符编码方案 [RFC6365]。字符集由不区分大小写的令牌标识。
charset = token
字符集名称应根据 [RFC2978] 中定义的程序在IANA"字符集"注册表 (http://www.iana.org/assignments/character-sets) 中注册。
3.1.1.3. Canonicalization and Text Defaults (规范化和文本默认值)
互联网媒体类型以规范形式注册,以便在具有不同本机编码格式的系统之间实现互操作性。通过HTTP选择或传输的表示应采用规范形式,原因与多用途互联网邮件扩展 (Multipurpose Internet Mail Extensions, MIME) [RFC2045] 所描述的原因大致相同。但是,电子邮件部署的性能特征(即存储和转发消息到对等方)与HTTP和Web常见的性能特征(基于服务器的信息服务)有很大不同。此外,MIME为了与较旧的邮件传输协议兼容而设置的约束不适用于HTTP(参见附录A)。
MIME的规范形式要求"text"类型的媒体子类型使用CRLF作为文本换行符。HTTP允许传输带有单独的CR或LF表示换行符的文本媒体,前提是此类换行符对于整个表示是一致的。HTTP发送方可以生成,接收方必须能够解析,由CRLF、裸CR或裸LF组成的文本媒体中的换行符。此外,HTTP中的文本媒体不限于使用八位字节13和10分别表示CR和LF的字符集。这种关于换行符的灵活性仅适用于已分配"text"媒体类型的表示中的文本;它不适用于"multipart"类型或有效载荷主体之外的HTTP元素(例如,头字段)。
如果表示使用内容编码 (content-coding) 进行编码,则在编码之前,基础数据应采用上述定义的形式。
3.1.1.4. Multipart Types (多部分类型)
MIME提供了许多"multipart"类型——单个消息主体中一个或多个表示的封装。所有多部分类型共享一个公共语法,如 [RFC2046] 的Section 5.1.1中定义的,并包含一个边界参数作为媒体类型值的一部分。消息主体本身是一个协议元素;发送方必须只生成CRLF来表示主体部分之间的换行符。
HTTP消息框架不使用多部分边界作为消息主体长度的指示符,尽管它可能被生成或处理有效载荷的实现使用。例如,"multipart/byteranges"类型由HTTP定义,用于某些206 (Partial Content) 响应 [RFC7233]。
3.1.1.5. Content-Type (内容类型)
Content-Type 头字段指示关联表示的媒体类型:封装在消息有效载荷中的表示或选定的表示,由消息语义确定。指示的媒体类型定义了数据格式以及接收方如何处理该数据,在解码Content-Encoding指示的任何内容编码之后,在接收到的消息语义范围内。
Content-Type = media-type
媒体类型在Section 3.1.1.1中定义。该字段的示例为:
Content-Type: text/html; charset=ISO-8859-4
生成包含有效载荷主体的消息的发送方应在该消息中生成 Content-Type 头字段,除非封装的表示的预期媒体类型对发送方未知。如果不存在 Content-Type 头字段,接收方可以假定媒体类型为"application/octet-stream" ([RFC2046] Section 4.5.1),或检查数据以确定其类型。
在实践中,资源所有者并不总是正确配置其源服务器以为给定表示提供正确的 Content-Type,结果是某些客户端将检查有效载荷的内容并覆盖指定的类型。这样做的客户端可能会得出不正确的结论,这可能会暴露额外的安全风险(例如,"权限提升")。此外,通过检查数据格式无法确定发送方的意图:许多数据格式匹配多个仅在处理语义上不同的媒体类型。鼓励实施者提供一种在使用时禁用此类"内容嗅探" (content sniffing) 的方法。
3.1.2. Encoding for Compression or Integrity (用于压缩或完整性的编码)
3.1.2.1. Content Codings (内容编码)
内容编码值 (content coding values) 指示已应用或可以应用于表示的编码转换。内容编码主要用于允许在不丢失其底层媒体类型的标识和不丢失信息的情况下压缩或以其他方式有用地转换表示。通常,表示以编码形式存储,直接传输,仅由接收方解码。
content-coding = token
所有内容编码值都不区分大小写,应在"HTTP内容编码注册表" (HTTP Content Coding Registry) 中注册,如Section 8.4中定义。它们在 Accept-Encoding (Section 5.3.4) 和 Content-Encoding (Section 3.1.2.2) 头字段中使用。
本规范定义了以下内容编码值:
-
compress (和 x-compress):UNIX"compress"数据格式 [Welch]。历史遗留;不建议在新应用程序中使用。
-
deflate:"zlib"格式 [RFC1950] 与"deflate"压缩机制 [RFC1951] 的组合。
-
gzip (和 x-gzip):带有32位CRC的LZ77编码 [RFC1952]。
-
identity:"identity"编码(无转换)。此编码仅在Accept-Encoding头字段中使用,不应在
Content-Encoding头字段中使用。
3.1.2.2. Content-Encoding (内容编码)
Content-Encoding 头字段指示除了媒体类型固有的编码之外,还对表示应用了哪些内容编码,因此必须应用哪些解码机制才能获得 Content-Type 头字段引用的媒体类型中的数据。Content-Encoding 主要用于允许在不丢失其底层媒体类型标识的情况下压缩表示的数据。
Content-Encoding = 1#content-coding
其使用示例为:
Content-Encoding: gzip
如果一个或多个编码已应用于表示,则应用编码的发送方必须生成 Content-Encoding 头字段,该字段按应用顺序列出内容编码。有关编码参数的其他信息可以由本规范未定义的其他头字段提供。
与Transfer-Encoding (Section 3.3 of [RFC7230]) 不同,Content-Encoding 中列出的编码是表示的特征;表示以编码形式定义,有关表示的所有其他元数据都是关于编码形式的,除非在元数据定义中另有说明。通常,表示仅在渲染或类似使用之前才被解码。
如果媒体类型包含固有编码,例如始终压缩的数据格式,则即使该编码恰好与其中一个内容编码使用相同的算法,也不会在 Content-Encoding 中重申该编码。只有在出于某种奇怪的原因,第二次应用该编码以形成表示时,才会列出这样的内容编码。同样,源服务器可能选择将相同的数据发布为多个仅在编码是定义为 Content-Type 的一部分还是 Content-Encoding 的一部分上有所不同的表示,因为某些用户代理在处理每个响应时会有不同的行为(例如,打开"另存为..."对话框而不是自动解压缩和渲染内容)。
如果请求消息中的表示具有不可接受的内容编码,源服务器可以使用状态码415 (Unsupported Media Type) 进行响应。
3.1.3. Audience Language (受众语言)
3.1.3.1. Language Tags (语言标签)
语言标签 (language tag),如 [RFC5646] 中定义,标识人类为了向其他人传达信息而使用的口头、书面或以其他方式传达的自然语言。计算机语言被明确排除在外。
HTTP在 Accept-Language 和 Content-Language 头字段中使用语言标签。Accept-Language 使用Section 5.3.5中定义的更广泛的language-range生成规则,而 Content-Language 使用下面定义的language-tag生成规则。
language-tag = <Language-Tag, see [RFC5646], Section 2.1>
语言标签是一个或多个不区分大小写的子标签序列,每个子标签由连字符("-",%x2D)分隔。在大多数情况下,语言标签由标识相关语言的广泛家族的主要语言子标签(例如,"en" = 英语)组成,后面可以选择跟随一系列细化或缩小该语言范围的子标签(例如,"en-CA" = 在加拿大使用的英语变体)。语言标签中不允许有空格。示例标签包括:
en, en-US, en-cockney, i-cherokee, x-pig-latin
有关更多信息,请参见 [RFC5646]。
3.1.3.2. Content-Language (内容语言)
Content-Language 头字段描述表示的预期受众的自然语言。请注意,这可能不等同于表示中使用的所有语言。
Content-Language = 1#language-tag
语言标签在Section 3.1.3.1中定义。Content-Language 的主要目的是允许用户根据用户自己的首选语言来识别和区分表示。因此,如果内容仅适用于丹麦语读者,则适当的字段为:
Content-Language: da
如果未指定 Content-Language,则默认情况下内容适用于所有语言受众。这可能意味着发送方不认为它特定于任何自然语言,或者发送方不知道它适用于哪种语言。
对于面向多个受众的内容,可以列出多种语言。例如,同时以原始毛利语和英语版本呈现的"怀唐伊条约" (Treaty of Waitangi) 的演绎将要求:
Content-Language: mi, en
但是,仅仅因为表示中存在多种语言并不意味着它适用于多个语言受众。例如,初学者语言入门书,如"拉丁语第一课" (A First Lesson in Latin),显然适用于英语读者。在这种情况下,Content-Language 应该只包括"en"。
Content-Language 可以应用于任何媒体类型——它不限于文本文档。
3.1.4. Identification (标识)
3.1.4.1. Identifying a Representation (标识表示)
当在消息有效载荷中传输完整或部分表示时,发送方通常希望提供或接收方希望确定与该表示对应的资源的标识符。
对于请求消息:
-
如果请求具有
Content-Location头字段,则发送方断言有效载荷是由Content-Location字段值标识的资源的表示。但是,除非可以通过其他方式(本规范未定义)进行验证,否则不能信任此类断言。该信息可能仍然对修订历史链接有用。 -
否则,有效载荷未标识。
对于响应消息,按顺序应用以下规则,直到找到匹配项:
-
如果请求方法是GET或HEAD,响应状态码是200 (OK)、204 (No Content)、206 (Partial Content) 或304 (Not Modified),则有效载荷是由有效请求URI (Section 5.5 of [RFC7230]) 标识的资源的表示。
-
如果请求方法是GET或HEAD,响应状态码是203 (Non-Authoritative Information),则有效载荷是由中间体提供的目标资源的潜在修改或增强表示。
-
如果响应具有
Content-Location头字段,并且其字段值是对与有效请求URI相同的URI的引用,则有效载荷是由有效请求URI标识的资源的表示。 -
如果响应具有
Content-Location头字段,并且其字段值是对与有效请求URI不同的URI的引用,则发送方断言有效载荷是由Content-Location字段值标识的资源的表示。但是,除非可以通过其他方式(本规范未定义)进行验证,否则不能信任此类断言。 -
否则,有效载荷未标识。
3.1.4.2. Content-Location (内容位置)
Content-Location 头字段引用一个URI,该URI可用作与此消息有效载荷中的表示对应的特定资源的标识符。换句话说,如果在此消息生成时对此URI执行GET请求,则200 (OK) 响应将包含与此消息中封装为有效载荷的表示相同的表示。
Content-Location = absolute-URI / partial-URI
Content-Location 值不是有效请求URI (Section 5.5 of [RFC7230]) 的替代品。它是表示元数据。它具有与 [RFC2557] 的Section 4中为MIME主体部分定义的同名头字段相同的语法和语义。但是,它在HTTP消息中的出现对HTTP接收方有一些特殊含义。
如果 Content-Location 包含在2xx (Successful) 响应消息中,并且其值(转换为绝对形式后)引用的URI与有效请求URI相同,则接收方可以将有效载荷视为该资源在消息发起日期指示的时间的当前表示。对于GET (Section 4.3.1) 或HEAD (Section 4.3.2) 请求,这与服务器未提供 Content-Location 时的默认语义相同。对于状态更改请求,如PUT (Section 4.3.4) 或POST (Section 4.3.3),这意味着服务器的响应包含该资源的新表示,从而将其与可能仅报告操作的表示区分开来(例如,"它成功了!")。这允许创作应用程序更新其本地副本,而无需后续的GET请求。
如果 Content-Location 包含在2xx (Successful) 响应消息中,并且其字段值引用的URI与有效请求URI不同,则源服务器声称该URI是与封装表示对应的不同资源的标识符。只有当两个标识符共享相同的资源所有者时,才能信任此类声称,而这无法通过HTTP以编程方式确定。
-
对于对GET或HEAD请求的响应,这表明有效请求URI引用的资源受内容协商的约束,并且
Content-Location字段值是所选表示的更具体的标识符。 -
对于对POST请求的201 (Created) 响应,
Content-Location字段值是对与提交的数据对应的资源的引用(即,与有效请求URI标识的资源不同的资源)。 -
否则,此类
Content-Location指示此有效载荷是请求消息目标以外的某个资源的表示,并且对HTTP没有其他意义。
如果 Content-Location 包含在响应消息中,并且表示由多个部分组成,如"multipart/*"的 Content-Type 所示,则每个部分可以有自己的 Content-Location 来指示与该部分对应的资源。否则,Content-Location 仅适用于整个有效载荷。
3.2. Representation Data (表示数据)
与HTTP消息关联的表示数据要么作为消息的有效载荷主体提供,要么由消息语义和有效请求URI引用。表示数据采用由表示元数据头字段定义的格式和编码。
表示数据的数据类型通过头字段 Content-Type 和 Content-Encoding 确定。这些定义了一个两层有序编码模型:
representation-data := Content-Encoding( Content-Type( bits ) )
3.3. Payload Semantics (有效载荷语义)
一些HTTP消息将完整或部分表示作为消息"有效载荷" (payload) 传输。在某些情况下,有效载荷可能仅包含关联表示的头字段(例如,对HEAD的响应)或仅包含表示数据的某些部分(例如,206 (Partial Content) 状态码)。
请求中有效载荷的目的由方法语义定义。例如,PUT请求 (Section 4.3.4) 的有效载荷中的表示表示目标资源的期望状态(如果请求成功应用),而POST请求 (Section 4.3.3) 的有效载荷中的表示表示要由目标资源处理的信息。
在响应中,有效载荷的目的由请求方法和响应状态码定义。例如,对GET (Section 4.3.1) 的200 (OK) 响应的有效载荷表示目标资源的当前状态,如在消息发起日期 (Section 7.1.1.2) 观察到的那样,而对POST的相同状态码的响应中的有效载荷可能表示处理结果或应用处理后目标资源的新状态。
在以下情况下,有效载荷被认为是"完整的" (complete):
-
已接收到整个头部分,并且指示的有效载荷主体长度为零;
-
分块有效载荷 (chunked payload) 在分块主体部分完成时完成(由零长度块和终止CRLF指示);
-
当连接关闭时,未知有效载荷长度完成。
3.4. Content Negotiation (内容协商)
当响应传达有效载荷信息时,无论是指示成功还是错误,源服务器通常有不同的方式来表示该信息;例如,以不同的格式、语言或编码。同样,不同的用户或用户代理可能具有不同的能力、特征或偏好,这些可能会影响在可用的表示中,哪个表示最适合交付。因此,HTTP提供了内容协商机制。
本规范定义了三种可以在协议中可见的内容协商模式:"主动" (proactive),其中服务器根据用户代理声明的偏好选择表示;"反应性" (reactive),其中服务器提供表示列表供用户代理选择;以及"请求内容" (request content),其中用户代理为请求有效载荷选择表示。其他内容协商模式包括"条件内容" (conditional content),其中表示由多个部分组成,这些部分根据用户代理参数选择性地呈现,"活动内容" (active content),其中表示包含根据用户代理特征发出额外(更具体)请求的脚本,以及"透明内容协商" (Transparent Content Negotiation) ([RFC2295]),其中内容选择由中间体执行。这些模式不是互斥的,每种模式在适用性和实用性方面都有权衡。
请注意,在所有情况下,HTTP都不知道资源语义。源服务器随时间和内容协商的不同维度响应请求的一致性,以及资源观察到的表示随时间的"相同性",完全由选择或生成这些响应的实体或算法确定。HTTP不关注幕后的人。
3.4.1. Proactive Negotiation (主动协商)
当用户代理在请求中发送内容协商偏好以鼓励位于服务器的算法选择首选表示时,称为主动协商 (proactive negotiation)(又称服务器驱动协商)。选择基于响应的可用表示(它可能变化的维度,如语言、内容编码等)与请求中提供的各种信息的比较,包括Section 5.3的显式协商字段和隐式特征,例如客户端的网络地址或 User-Agent 字段的部分内容。
当从可用表示中选择的算法难以向用户代理描述,或者当服务器希望将其"最佳猜测"与第一个响应一起发送给用户代理(希望避免后续请求的往返延迟,如果"最佳猜测"对用户足够好)时,主动协商是有利的。为了改进服务器的猜测,用户代理可以发送描述其偏好的请求头字段。
主动协商有严重的缺点:
-
服务器不可能准确地确定对任何给定用户来说什么可能是"最好的",因为这需要完全了解用户代理的能力和响应的预期用途(例如,用户是想在屏幕上查看它还是在纸上打印它?);
-
让用户代理在每个请求中描述其能力既可能非常低效(考虑到只有一小部分响应具有多个表示),又可能对用户的隐私构成潜在风险;
-
它使源服务器的实现和生成请求响应的算法变得复杂;并且,
-
它限制了共享缓存的响应的可重用性。
用户代理不能依赖主动协商偏好被一致地遵守,因为源服务器可能不为请求的资源实现主动协商,或者可能决定发送不符合用户代理偏好的响应比发送406 (Not Acceptable) 响应更好。
Vary头字段 (Section 7.1.4) 通常在受主动协商约束的响应中发送,以指示在选择算法中使用了请求信息的哪些部分。
3.4.2. Reactive Negotiation (反应性协商)
使用反应性协商 (reactive negotiation)(又称代理驱动协商),最佳响应表示的选择(无论状态码如何)由用户代理在从源服务器接收到包含替代表示资源列表的初始响应后执行。如果用户代理对初始响应表示不满意,它可以对一个或多个替代资源执行GET请求,根据列表中包含的元数据选择,以获得该响应的不同形式的表示。替代方案的选择可能由用户代理自动执行,或由用户从生成的(可能是超文本)菜单中手动选择。
请注意,上述内容是指响应的表示,一般来说,不是资源的表示。替代表示不一定是同一资源的表示,尽管它们可能是。
服务器可能选择不发送初始表示,而不是替代方案列表,从而指示优先由用户代理进行反应性协商。例如,具有300 (Multiple Choices) 和406 (Not Acceptable) 状态码的响应中列出的替代方案包括有关可用表示的信息,以便用户或用户代理可以通过进行选择来做出反应。
当响应将在常用维度(如类型、语言或编码)上变化时,当源服务器无法通过检查请求来确定用户代理的能力时,以及通常当使用公共缓存来分配服务器负载和减少网络使用时,反应性协商是有利的。
反应性协商的缺点是向用户代理传输替代方案列表,如果在头部分中传输,会降低用户感知的延迟,并且需要第二个请求才能获得替代表示。此外,本规范没有定义支持自动选择的机制,尽管它不阻止开发这样的机制作为扩展。
3.4.3. Request Content Negotiation (请求内容协商)
当内容协商偏好在服务器对客户端的响应中发送时,客户端可以使用这些偏好来确定哪个表示最适合对该资源的后续请求。这称为请求内容协商 (request content negotiation)(又称"作为服务质量的内容协商")。
例如,415 (Unsupported Media Type) 响应中的 Accept 头字段可能指示在对该资源的PUT请求中接受哪些媒体类型,或者响应的 Accept-Language 字段可能指示服务的语言偏好。