HTTP必知-内容协商(Content Negotiation)

写在前面

在HTTP协议中,内容协商(Content Negotiation)是一种对使用同一URI的不同资源表述方式的机制,这句话有些绕,简而言之,就是使用同一个URI访问资源,响应消息体(Response Body)可以有不同的内容类型。这样机制的好处在于,对于同一资源,可以适配不同的客户端进行消费,以及不同的国家语言环境进行消费。例如:REST API可以给客户端提供JSON、XML等内容类型;Web网站的一个文档,可以给不同国家、语言用户提供不同的语言或编码版本。这样我们简化了HTTP Endpoint,通过内容协商机制可以获取到客户端想获取的内容。

“内容协商”的核心概念

一个特定的文档或文件,在此我们将其称作为资源,当一个客户端(应用程序或浏览器)想获取这个资源时,它将通过资源的URI(唯一资源标识符)进行请求访问。服务端收到请求后,将该URI对应的资源给客户端,该资源可能有多重表述形式,例如不同内容类型(JSON、XML)或不同语言(中文、英文),那么客户端需要跟服务端进行协商,以获取其期望的特定表述形式,这个过程便是“内容协商”。“内容协商”的方式有两种:

  • 服务端驱动/主动:通常选用该方式。客户端通过在请求的特定HTTP头部字段进行设置(或URL后缀、请求参数方式,但推荐使用头部字段方式),从而使得服务端知道该客户端期望获取资源的表述形式,最后由服务端决定到底返回何种表述形式给客户端。
  • 客户端驱动/被动:服务端返回资源的表述形式列表,由客户端自行选择需要哪种表述形式。

后面又出现了其它的内容协商方式,如透明方式等,但主要还是以上两种方式,实际工程使用中,建议大家使用第一种方式。

服务端驱动内容协商,又称主动模式

在服务端驱动模式(又称主动模式)下,客户端或浏览器在跟服务端请求资源时,在请求头部会有些特定的字段来表明期望表述形式。服务端使用客户端请求头部这些字段,来根据自己内部的算法来选取最为适合该请求的表述形式。如下图所示:

HTTP Content Negotiation Server-Driven

HTTP/1.1 协议标准里面定义了一些标准的用于服务端驱动内容协商模式的头部字段,如Accept、Accept-Charset、Accept-Encoding、Accept-Language等。严格来说User-Agent并不属于该列表之上的字段,因此我们在实践内容协商的实现时,使用User-Agent字段进行判断处理并不推荐。服务端在处理请求返回响应给客户端时,在响应头部Vary字段告知客户端哪个头部字段被用于内容协商了。接下来我们说下用于内容协商的请求头部字段。

头部字段:Accept

Accept字段是表示客户端可以消费资源的内容类型列表,以MIME方式表示,以逗号最为分隔。MIME后面可以跟上Quality factor(q因子,表明该类型在列表中的优先级),以分号分隔。下面列些常见的Accept字段示例:

  • Accept: text/html
  • Accept: text/html, application/xhtml+xml
  • Accept: application/json, application/xml
  • Accept: text/html, application/xhtml+xml, application/xml;q=0.9, /;q=0.8

客户端驱动内容协商,又称被动模式

服务端驱动模式有个显著问题,扩展性不是特别好,我们在使用请求头部字段的时候,每个字段代表一种含义,如果客户端需要添加屏幕大小、分辨率等信息,就要创建新的HTTP头部字段,每个请求发送的时候都得附带上,请求量小的情况下还好,如果海量请求的情况下,着实是浪费带宽、降低性能啊。

所以HTTP协议里面还有一种内容协商方式,便是客户端模式,又称为被动模式。服务端返回给客户端一组链接,这些链接便是客户端要访问资源的可以支持的内容类型链接,然后客户端选择一个最为适合自己的来使用。

HTTP Content Negotiation Agent-Driven

HTTP/1.1 协议里面定义了300(Multiple Choices)和406(Not Acceptable)这两个状态码共该模式使用,在这种模式下,需要发起多次请求才能够拿到客户端想要的内容,这样降低了客户端的可用性。


junq

Put a dent in the universe.