Content negotiation
在HTTP中,内容协商是用于在相同URI处为资源提供不同表示的机制,以便用户代理可以指定哪一个最适合用户(例如,文档的哪种语言,哪种图像格式或哪些内容编码)。
内容谈判原则
一个特定的文档被称为资源。当客户想要获得它时,它会使用它的URL来请求它。服务器使用此URL来选择它提供的变体之一 - 每个变体称为表示 - 并将此特定表示形式返回给客户机。整体资源以及每个表示都有一个特定的URL。在调用资源时如何选择特定的表示形式取决于内容协商,并且客户端和服务器之间有多种协商方式。
通过以下两种机制中的一种来确定最适合的表示:
- 客户端的特定HTTP头(服务器驱动的协商或主动协商),这是协商特定种类资源的标准方式。
- 服务器(代理驱动的协商或反应式协商)提供的
300
(多选)或406
(不可接受的)HTTP响应代码,用作后备机制。
多年来,已经提出了其他内容协商提议,如透明内容协商和Alternates
标题。他们未能获得牵引力而被抛弃。
服务器驱动的内容协商
在服务器驱动的内容协商或主动式内容协商中,浏览器(或任何其他类型的用户代理)会发送多个HTTP标头以及URL。这些标题描述了用户的首选。服务器使用它们作为提示,并且内部算法选择向客户端提供最佳内容。该算法是特定于服务器的,并未在标准中定义。例如,请参阅Apache 2.2协商算法。
HTTP / 1.1标准定义了启动服务器驱动的协商(标准报头的列表Accept
,Accept-Charset
,Accept-Encoding
,Accept-Language
)。虽然严格来说User-Agent
不在此列表中,但它有时也用于发送所请求资源的特定表示,尽管这不被认为是一种好的做法。服务器使用Vary
头来指示它实际用于内容协商的头(或更确切地说是关联的响应头),以便缓存可以最佳地工作。
除此之外,还有一个实验性的建议是在可用标题列表中添加更多标题,称为客户端提示。客户端提示通知用户代理运行的设备类型(例如,如果它是台式计算机或移动设备)。
即使服务器驱动的内容协商是就资源的特定表示达成一致的最常见方式,但它有几个缺点:
- 服务器不具有浏览器的全部知识。即使使用客户端提示扩展,它也不完全了解浏览器的功能。与客户做出选择的被动内容协商不同,服务器选择总是有些随意。
- 客户端的信息非常详细(HTTP / 2头压缩缓解了这个问题)和隐私风险(HTTP指纹)
- 随着给定资源的多个表示被发送,共享缓存效率较低,服务器实现更为复杂。
Accept
头
该Accept
标题列出了MIME类型的媒体资源代理愿意处理。它是以逗号分隔的MIME类型列表,每个列表都与质量因子相结合,该参数指示不同MIME类型之间的相对偏好程度。
Accept
头是由浏览器,或任何其它用户代理限定,并且可以根据环境而变化,像取HTML页面或图像,视频,或脚本:提取在输入的地址的文档时它是不同杆或元件经由连接<img>
,<video>
或<audio>
元件。浏览器可以自由使用他们认为最合适的标题的值; 常见浏览器默认值的详尽列表可用。
Accept-CH
头
这是名为Client Hints的实验性技术的一部分,目前仅在Chrome 46及更高版本中实施
实验Accept-CH
列出了服务器可用于选择适当响应的配置数据。有效值是:
值 | 含义 |
---|---|
DPR | 指示客户端的设备像素比率。 |
视口宽度 | 指示CSS像素中的布局视口宽度。 |
宽度 | 指示物理像素中的资源宽度(换句话说,是图像的固有大小)。 |
Accept-Charset
头
Accept-Charset
标题指示到什么样的字符编码由用户代理理解服务器。传统上,它针对浏览器的每个区域设置为不同的值,例如ISO-8859-1,utf-8;q=0.7,*;q=0.7
西欧语言区域设置。
UTF-8现在得到了很好的支持,成为字符编码的首选方式,并通过减少基于配置的熵来保证更好的隐私,大多数浏览器都忽略了Accept-Charset
标题:Internet Explorer 8,Safari 5,Opera 11和Firefox 10已经放弃这个标题。
Accept-Encoding
头
Accept-Encoding
报头定义内容编码的可接受的(支持的压缩)。该值是br, gzip;q=0.8
指示编码值的优先级的q因子列表(例如:)。默认值identity
是最低优先级(除非另有声明)。
压缩HTTP消息是提高Web站点性能的最重要方式之一,它缩小了传输数据的大小并更好地利用了可用带宽; 浏览器总是发送这个头文件,服务器应该被配置为遵守并使用压缩。
Accept-Language
头
所述Accept-Language
报头用于指示所述用户的语言偏好。它是一个包含质量因素的值列表(如:"de, en;q=0.7
“)。默认值通常根据用户代理图形界面的语言设置,但大多数浏览器允许设置不同的语言首选项。
由于基于配置的熵增加,可以使用修改后的值来为用户指纹,不建议对其进行更改,并且网站不能相信此值以反映用户的实际愿望。网站设计人员不能通过使用通过此标头进行语言检测而过于热心,因为这会导致糟糕的用户体验:
- 他们应该始终提供一种方法来克服服务器选择的语言,例如通过在网站上提供语言菜单。大多数用户代理为
Accept-Language
标题提供一个默认值,根据用户界面语言进行调整,最终用户通常不会对其进行修改,或者不知道如何修改,或者无法完成,就像在网吧中一样。
- 一旦用户重写了服务器选择的语言,站点就不应该再使用语言检测,并应该坚持使用明确选择的语言。换句话说,只有网站的入口页面应该使用这个头文件选择适当的语言。
User-Agent
头
虽然这个标题有合法的用途来选择内容,但依赖它来定义用户代理支持哪些功能被认为是不好的做法。
User-Agent
报头标识浏览器发送请求。该字符串可能包含空格分隔的产品标记和注释列表。
一个产品标记 是一个后面是“ /
”和版本号一样Firefox/4.0.1
的名字。用户代理想要的可能有很多。一个注释是括号分隔的一个任意字符串。显然括号不能用在那个字符串中。评论的内部格式不是由标准定义的,尽管多个浏览器在其中放置了几个标记,以' ;
' 分隔。
Vary
响应报头
Accept-*
与客户端发送的先前标题相反,Vary
HTTP标题是由Web服务器在其响应中发送的。它表示服务器驱动的内容协商阶段服务器使用的标头列表。标题是需要的,以便通知缓存的决策标准,以便它可以重现它,允许缓存功能,同时防止向用户提供错误的内容。
' *
' 的特殊值意味着服务器驱动的内容协商也使用信息中未传送的信息来选择适当的内容。
Vary
在HTTP的1.1版本中加入报头和是必要的,以便允许高速缓存以适当地工作。为了与服务器驱动的内容协商一起工作,高速缓存需要知道服务器使用哪个标准来选择传输的内容。这样,缓存就可以重播算法,并能够直接提供可接受的内容,而无需向服务器提出更多请求。显然,通配符' *
'阻止了缓存的发生,因为缓存不知道它背后是什么元素。
代理驱动的协商
服务器驱动的协商有一些缺点:它不能很好地扩展。谈判中使用的每个功能都有一个标题。如果您想使用屏幕尺寸,分辨率或其他尺寸,则必须创建新的HTTP标头。发送标题必须在每个请求上完成。这并不是问题很少,只有很少的标题,但随着它们的最终乘法,消息大小会导致性能下降。发送的报头越精确,发送的熵就越多,允许更多的HTTP指纹识别和相应的隐私问题。
从HTTP开始,协议允许另一种协商类型:代理驱动协商或反应式协商。在这个协商中,当面对不明确的请求时,服务器发送一个包含指向可用替代资源链接的页面。用户被呈现资源并选择要使用的资源。
不幸的是,HTTP标准没有指定允许在可用资源之间进行选择的页面格式,这阻止了轻松自动化该过程。除了回到服务器驱动的协商之外,这种方法几乎总是与脚本一起使用,尤其是在JavaScript重定向的情况下:在检查协商标准后,脚本执行重定向。第二个问题是需要多一个请求才能获取真实资源,从而减慢了用户对资源的可用性。