Skip to main content

12. Content Negotiation (内容协商)

大多数 HTTP 响应包含一个实体,该实体包含供人类用户解释的信息。自然地,希望向用户提供与请求相对应的"最佳可用"实体。不幸的是,对于服务器和缓存来说,并非所有用户对什么是"最佳"都有相同的偏好,并且并非所有用户代理都同样能够呈现所有实体类型。因此,HTTP 提供了几种"内容协商" (content negotiation) 机制——当有多个表示可用时,为给定响应选择最佳表示的过程。

注意:这不称为"格式协商" (format negotiation),因为替代表示可能是相同的媒体类型,但使用该类型的不同功能、使用不同的语言等。

任何包含实体主体的响应都可以 (MAY) 受到协商,包括错误响应。

HTTP 中可能有两种内容协商:服务器驱动的协商 (server-driven negotiation) 和代理驱动的协商 (agent-driven negotiation)。这两种协商是正交的,因此可以单独使用或组合使用。一种组合方法,称为透明协商 (transparent negotiation),发生在缓存使用源服务器提供的代理驱动的协商信息以便为后续请求提供服务器驱动的协商时。

12.1 Server-driven Negotiation (服务器驱动的协商)

如果响应的最佳表示的选择是由位于服务器的算法做出的,则称为服务器驱动的协商。选择基于响应的可用表示(它可以变化的维度;例如,语言、内容编码等)以及请求消息中特定头字段的内容或与请求有关的其他信息(例如客户端的网络地址)。

当从可用表示中选择的算法难以向用户代理描述时,或者当服务器希望将其"最佳猜测"与第一个响应一起发送给客户端时(希望避免后续请求的往返延迟,如果"最佳猜测"对用户来说足够好),服务器驱动的协商是有利的。为了改进服务器的猜测,用户代理可以 (MAY) 包括描述其对此类响应的偏好的请求头字段(Accept、Accept-Language、Accept-Encoding 等)。

服务器驱动的协商有缺点:

  1. 服务器不可能准确确定对任何给定用户什么可能是"最佳"的,因为这需要完全了解用户代理的功能和特定请求的预期用途。

  2. 让用户代理为每个请求描述其功能很麻烦(占用可用带宽),并且可能导致重大的隐私风险(用户指纹识别)。

  3. 它使源服务器的实现复杂化,并且使算法难以验证。

  4. 它可能限制公开可用的表示,当服务器算法对用户代理功能做出假设时。

HTTP/1.1 包括以下请求头字段,用于启用服务器驱动的协商:Accept (第 14.1 节)、Accept-Charset (第 14.2 节)、Accept-Encoding (第 14.3 节)、Accept-Language (第 14.4 节) 和 User-Agent (第 14.43 节)。但是,源服务器不限于这些维度,并且可以 (MAY) 根据请求的任何方面变化响应,包括未列出的请求头字段或请求中未列出的扩展头字段。

Vary 头字段可用于表达在选择表示时使用的参数,这可能对用户代理在理解缓存的适用性时有用(参见第 13.6 节)和生成 Vary 头字段的服务器有用(参见第 14.44 节)。

12.2 Agent-driven Negotiation (代理驱动的协商)

使用代理驱动的协商,在接收到初始响应后,对响应的最佳表示的选择由用户代理执行。选择基于可用表示的列表(包括它们的 URI 和其区别特征)以及用户或用户代理的偏好。

代理驱动的协商在以下情况下是有利的:当响应在可用表示的维度上变化时,源服务器无法通过检查请求来准确确定什么对用户代理最好,或者当公共缓存用于通过配置用户代理来降低服务器负载和网络使用时。

代理驱动的协商存在以下缺点:需要第二个请求才能获得最佳替代表示。仅当缓存能够提供正确的响应时(已经缓存)或者资源的列表很小时,这个第二个请求才会增加可感知的延迟。此外,这个规范没有为列表提供标准,也没有为用户代理自动选择最佳选择提供标准,本规范没有定义此类自动选择的任何标准。

HTTP/1.1 定义了 300 (Multiple Choices) 和 406 (Not Acceptable) 状态码,供服务器在执行代理驱动的协商时使用。

12.3 Transparent Negotiation (透明协商)

透明协商是代理驱动的和服务器驱动的协商的组合。当缓存向用户代理提供服务器驱动的协商时使用源服务器提供的内容协商信息。

透明协商的优点是它消除了在源服务器不可用时的一次请求往返。

透明协商的缺点是,如果内容协商信息不完整或不准确,它可能导致选择不正确的表示。

HTTP/1.1 不定义任何透明协商的头。