跳到主要内容

12. 内容协商 (Content Negotiation)

12 Content Negotiation

大多数 HTTP response 都包含一个 entity, 其中有供人类用户解释的信息. 自然, 最理想的是向用户提供与 request 对应的 "best available" entity. 不幸的是, 对 server 和 cache 而言, 并非所有用户对什么是 "best" 都有相同偏好, 也并非所有 user agent 都同样能够呈现所有 entity type. 因此, HTTP 为若干 "content negotiation" 机制作出规定 -- 当存在多个 representation 可用时, 为给定 response 选择最佳 representation 的过程.

  注意: 这不称为 "format negotiation", 因为替代 representation 可能具有相同 media type,
但使用该类型的不同能力, 或采用不同语言, 等等.

任何包含 entity-body 的 response MAY 受协商约束, 包括 error response.

HTTP 中可能存在两种 content negotiation: server-driven negotiation 和 agent-driven negotiation. 这两种协商彼此正交, 因此可以分别使用, 也可以组合使用. 一种组合方式称为 transparent negotiation: 当 cache 使用 origin server 提供的 agent-driven negotiation 信息, 来为该 resource 后续 request 提供 server-driven negotiation 时, 就会发生这种协商.

12.1 Server-driven Negotiation

如果 response 最佳 representation 的选择由位于 server 的算法完成, 则称为 server-driven negotiation. 选择基于 response 的可用 representation (它可变化的维度, 例如 language, content-coding 等), 以及 request message 中特定 header field 的内容, 或与 request 相关的其他信息 (如 client 的 network address).

当从可用 representation 中选择的算法难以向 user agent 描述时, 或者当 server 希望随第一个 response 向 client 发送自己的 "best guess" 时 (希望如果该 "best guess" 对用户足够好, 就避免后续 request 的往返延迟), server-driven negotiation 是有利的. 为改善 server 的猜测, user agent MAY 包含 request header field (Accept, Accept-Language, Accept-Encoding 等), 用来描述它对此类 response 的偏好.

Server-driven negotiation 有一些缺点:

  1. server 不可能准确确定对任何给定用户什么可能是 "best",
因为这需要完整了解 user agent 的能力以及 response 的预期用途
(例如, 用户是想在屏幕上查看还是在纸上打印?).

2. 让 user agent 在每个 request 中描述其能力, 既可能非常低效
(考虑到只有很小比例的 response 具有多个 representation), 也可能侵犯用户隐私.

3. 它使 origin server 的实现和生成 request response 的算法复杂化.

  4. 它可能限制 public cache 为多个用户 request 使用同一个 response 的能力.

HTTP/1.1 包含以下 request-header field, 用于通过描述 user agent 能力和用户偏好来启用 server-driven negotiation: Accept (第 14.1 节), Accept-Charset (第 14.2 节), Accept-Encoding (第 14.3 节), Accept-Language (第 14.4 节) 和 User-Agent (第 14.43 节). 但是, origin server 不限于这些维度, MAY 基于 request 的任何方面改变 response, 包括 request-header field 之外的信息, 或本规范未定义的 extension header field 中的信息.

Vary header field 可用于表达 server 用来选择受 server-driven negotiation 约束的 representation 的参数. cache 对 Vary header field 的使用见第 13.6 节, server 对 Vary header field 的使用见第 14.44 节.

12.2 Agent-driven Negotiation

使用 agent-driven negotiation 时, response 最佳 representation 的选择由 user agent 在收到 origin server 的初始 response 后执行. 选择基于初始 response 的 header field 或 entity-body 中包含的可用 representation 列表, 每个 representation 由其自己的 URI 标识. 可由 user agent 自动在 representation 中选择 (如果 user agent 能够这样做), 也可由用户从生成的 (可能是 hypertext) 菜单中手动选择.

当 response 会在常用维度 (如 type, language 或 encoding) 上变化, 当 origin server 无法通过检查 request 确定 user agent 能力, 以及通常使用 public cache 来分担 server 负载并减少网络使用时, agent-driven negotiation 是有利的.

Agent-driven negotiation 的缺点是需要第二个 request 才能获得最佳替代 representation. 只有在使用 caching 时, 这个第二 request 才高效. 此外, 本规范不定义任何支持自动选择的机制, 但也不阻止开发此类机制作为扩展并在 HTTP/1.1 中使用.

当 server 不愿或无法使用 server-driven negotiation 提供变化 response 时, HTTP/1.1 定义了 300 (Multiple Choices) 和 406 (Not Acceptable) status code 来启用 agent-driven negotiation.

12.3 Transparent Negotiation

Transparent negotiation 是 server-driven 和 agent-driven negotiation 的组合. 当 cache 获得某种形式的 response 可用 representation 列表 (如 agent-driven negotiation 中), 且 cache 完全理解变化维度时, cache 就能够代表 origin server 为该 resource 的后续 request 执行 server-driven negotiation.

Transparent negotiation 的优点是分散了原本需要 origin server 承担的协商工作, 并且当 cache 能够正确猜测适当 response 时, 消除了 agent-driven negotiation 的第二 request 延迟.

本规范不定义任何 transparent negotiation 机制, 但也不阻止开发此类机制作为可在 HTTP/1.1 中使用的扩展.