考虑这样的一种情况,服务器对于某个资源有好几个不同的版本,当浏览器发送请求过来的时候,服务器会提供一个最合适的版本给浏览器。
举个例子来说,对于某个网站的主页,服务器可能有“汉语”、“英语”、“法语”,好几个不同的版本,当浏览器发送请求过来的时候,服务器需要判断,到底给浏览器哪个语言的版本比较合适。
服务器为了确定这个比较合适的版本,需要和浏览器进行一些沟通,这个沟通的过程,就是内容协商。
内容协商的过程
那服务器和浏览器是如何进行沟通的呢?目前主流的方法是通过使用请求中一些头部信息来完成这一沟通的,浏览器在发送请求的请求中放置一些头部信息,告诉服务器我需要什么样的数据,然后服务器返回相应版本的数据。
主要使用的请求头有:
1、Accept
2、Accept-Language
3、Accept-Encoding
Accept
accept
主要是告诉服务器,浏览器能够接受什么样的数据类型,比如能够接受图片类型的数据,或者能够接受一个xml文件,当然了,浏览器一般可以接受的数据类型是有很多种的,因此一个accept
头信息可能是:
accept: text/html, image/jpeg, image/gif, application/pdf
他表示浏览器可以接受html
、jpeg
图片、pdf
三种文件类型,根据这个头部,服务器在发送数据的时候,就要充分考虑浏览器能不能接受。
有的同学可能会问了,如果服务器发送了一份数据,浏览器怎么知道它是什么类型的数据呢?
服务器在给浏览器发送响应数据的时候,也会有头部信息,其中的一个头叫做Content-Type
,它会告诉浏览器,我发给你的是什么类型的数据。
那假如对于一份数据,服务器有不同数据类型的版本,即有html
类型的,也有jpeg
类型的,那么发送哪个版本比较合适呢?
浏览器在accept
中可以使用一个叫q
的参数,告诉服务器哪些数据类型比较推荐发送。
accept: text/html, image/jpeg; q=0.3, image/gif; q=0.5, application/pdf
如上,浏览器可以接受jpeg
、和gif
两种类型的图片,但是gif
类型的q
值是0.5
,比较高,q
值越高,表示这种数据类型越合适。
那对于text/html
并没有制定q
值,那这时候表会取q
的默认值1,也就是说,text/html
等同于text/html; q=1
。
Accept-Charset
Accept-Charset
表示浏览器支持的字符集编码,比如支持utf-8
或者gb2312
:
Accept-Charset: ISO-8859-1,gb2312,utf-8;q=0.7,*;q=0.3
上面表示,浏览器支持ISO-8859-1
、gb2312
、utf-8
、*
等字符集,*
表示其他任意的字符集,其中的q
值表示,浏览器优先支持ISO-8859-1
、gb2312
字符集(默认的q
值为1),但是对于utf-8
字符集,它的支持权重为0.7
,其他字符集的权重为0.3
。
如果服务器那边有多个字符集版本的资源,那么发送的时候应该优选发送ISO-8859-1
,gb2312
这两个版本,假如服务器那边没有这两个版本那么就推荐发送utf-8
版本,实在不行,在发送其他的版本。
Accept-Encoding
在服务器发送数据给浏览器的时候,为了减少数据量,加快发送速度,服务器可以对数据进行压缩,由于服务器给浏览器发送的数据一般是html文本,对于这种文本数据,gzip
和deflate
这两种压缩算法都有比较好的压缩效果,可以达到原内容的40%。
Accept-Encoding
头就是用于浏览器告诉服务器,我希望你发送的数据是压缩过的,并且使用的压缩算法是我指定的:
Accept-Encoding: gzip; q=0.9, deflate; q=0.7
上面表示,浏览器支持gzip
和deflate
两种压缩算法,但是推荐服务器使用gzip
来压缩,因为它的q
值比较高,如果服务器发送过来的数据用的压缩算法浏览器不支持,那么就会出错。
还有一个问题是,浏览器怎么知道服务器用了何种压缩算法呢?
同样,服务器发送的响应中,有一个对应的头部:Content-Encoding
,会告诉浏览器,使用了什么样的压缩算法。
Accept-Language
如果一个网站支持多语言,一般会用到这个头信息,Accept-Language
主要是告诉服务器,当前用户比较擅长某种语言(中文、英文等),建议服务器发送这种语言的页面过来:
Accept-Language: zh-CN,fr-FR;q=0.5
zh-CN
表示中文,fr-FR
表示法语,上面表示,浏览器希望服务器发送zh-CN
中文版本的页面,因为它的q
值是1,如果没有中文的,那就发送法语版本的。
那浏览器根据什么来判断,什么语言比较合适用户呢?
浏览器一般会根据用户的操作系统(比如中国一般用的是中文版的操作系统)来给予语言方面的建议,有的浏览器本身就有各种语言的版本,也可以根据自己的版本信息来进行判断。
Vary
在网络当中,浏览器和服务器并不是直接进行通信的,在他们中间往往有很多的代理服务器:
浏览器=>代理服务器1=>代理服务器2=>...=>服务器
其中的代理服务器一般具有缓存的功能,现在假设,一个浏览器发送了请求,要求服务器提供压缩版本的数据,这个数据返回后,会被缓存在代理服务器中,然后,另外一个浏览器发送了请求同样数据的请求,但是不是压缩版本的,这个时候就容易发生问题。
当第二个请求到达代理服务器的时候,代理服务器发现请求的资源已经缓存起来了,就会把的压缩版本资源返回,这样就会导致错误。
Vary
头是服务器返回响应的时候,放置在响应头当中的,用来告诉这些代理服务器,当你缓存我的时候,需要补上对应的请求中的一些头信息作为我的版本信息,这样下个请求过来的时候,除了判断是不是相同的url
,还需要判断版本信息是不是一样的,最后来决定是不是使用缓存:
Vary: Accept-Encoding, Accept
上面的头信息是告诉代理服务器,缓存我(响应)的时候,你需要加上对应请求当中的Accept-Encoding
,Accept
信息,这样,如果下个请求当中的Accept-Encoding
,Accept
信息和缓存中的不一致,就会认为缓存不可用。
为什么代理服务器在检测版本的时候,不使用响应头Content-*
头呢?
原因是有些代理服务器会自动忽略掉响应当中的一些头,比如响应中的Content-Encoding
头,这样代理服务器就根本不知道这个响应数据是不是经过压缩的了。