Apache HTTP服务器 2.0版本
Apache支持HTTP/1.1规范中定义的内容协商, 以根据浏览器提供的设置选择不同媒介类型、语言、字符集和编码的最佳表现, 还有对来自浏览器的不完整内容协商信息作智能处理的能力。
内容协商由mod_negotiation
模块支持,
并被缺省地编译进服务器核心。
一个资源可能会有多种不同的表现形式,比如,可能会有不同语言或者媒体类型的版本甚至其组合。 最常用的选择方法是提供一个索引页以供选择。但是由于浏览器可以在请求头信息中提供其首选项的表现形式, 因此就有可能让服务器自动选择。比如,浏览器可以表明希望看见法语的信息,如果没有,英语的也行。 如需仅请求法语表现形式,浏览器可以发出:
Accept-Language: fr
注意:此首选项信息仅当存在可选的多种语言表现形式时有效。
这是一个更复杂的请求,浏览器表明,可以接受法语和英语,但最好是法语; 接受各种媒体类型,最好是HTML,纯文件或其他文本类型也可以; 最好是GIF或JPEG,其他媒体类型也可以,并允许其他媒体类型作为最终表现形式:
Accept-Language: fr; q=1.0, en; q=0.5
Accept: text/html; q=1.0, text/*; q=0.8, image/gif; q=0.6, image/jpeg; q=0.6, image/*; q=0.5, */*; q=0.1
Apache支持HTTP/1.1规范中定义的“服务器驱动”的内容协商, 可以完全地支持Accept, Accept-Language, Accept-Charset和Accept-Encoding请求头, 这些是RFC 2295和RFC2296中定义的实验协商协议,但是不支持这些RFC中定义的“功能协商”。
资源是一个在URI (RFC 2396)中定义的概念上的实体。 一个HTTP服务器,比如Apache,以表现形式提供对其名称空间中资源的访问, 各表现形式由已定义的媒体类型、字符集和编码的字节流构成。 任何一个特定的时刻,一个资源可以没有,或者有一个,或者有多个表现形式。 如果有多个表现形式存在,则称该资源是可协商的, 其各种表现形式术语称为变种, 而一个可协商的资源的各种变种的区别方式称为变元。
有两种途径向服务器提供有关各变种的信息,以实现对资源的协商:
*.var
文件)明确指定各变种的文件名;类型表是一个与命名为type-map
的处理器关联的文档
(或者是MIME类型application/x-type-map
,以向下兼容早期Apache的配置)。
要使用这个功能,必须在配置中建立处理器,以定义一个文件后缀为type-map
,
最好的方法是在配置文件中这样设置:
AddHandler type-map .var
类型表文件应该与所描述资源同名,且对每个有效变种有一个块,
各块由若干连续的HTTP格式头组成,各变种的块用空行分开,块中不允许有空行。
习惯上,类型表首部会有一个概要性质的组合描述块(这不是必须的,如果有也会被忽略)。
下例是一个描述资源foo
的命名为foo.var
的类型表文件:
URI: foo
URI: foo.en.html
Content-type: text/html
Content-language: en
URI: foo.fr.de.html
Content-type: text/html;charset=iso-8859-2
Content-language: fr, de
注意:即使Multiviews设置为on,类型表都优先于文件后缀。 可以对媒体类型用"qs"参数指示变种不同的资源品质。 下例,演示了这个图片的jpeg, gif和字符构图三个有效变种:
URI: foo
URI: foo.jpeg
Content-type: image/jpeg; qs=0.8
URI: foo.gif
Content-type: image/gif; qs=0.5
URI: foo.txt
Content-type: text/plain; qs=0.01
qs的取值范围是0.000到1.000, 取值为0.000的变种将不会被选择,没有指定qs值的变种会赋予其qs值为1.0。 qs表示一个变种相对于其他变种的“品质”, 比如在表现一张照片时,jpeg文件通常比字符构图有较高的还原品质; 而如果要表现的本来就是一个字符构图,那么当然字符构图会比jpeg文件有较高的还原品质。 因此,qs的值取决于变种所表现的资源本身。
mod_negotation typemap文档中有完整的HTTP头的列表。
MultiViews
是一个针对目录的选项,可以在access.conf
或者.htaccess
文件(如果正确设置了AllowOverride
)中的<Directory>
, <Location>
和<Files>
段中,用Options
指令来指定。注意,Options All
并不会设置MultiViews
,必须明确地指定。
MultiViews
的效果是:如果服务器收到/some/dir/foo
请求,而/some/dir/foo
并不存在,但是如果/some/dir
允许MultiViews
,则服务器会查找这个目录下的foo.*,有效地利用说明了这些文件的类型表,假定其媒体类型及内容编码符合客户的要求,并选择其中最合适的匹配返回给客户。
MultiViews
还可以在服务器索引一个目录时,用于DirectoryIndex
指令的文件搜索。如果设置了:
DirectoryIndex index
而index.html
和index.html3
并存,则服务器会作一个权衡;如果都没有,但是有index.cgi
,则服务器会执行它。
如果一个目录中没有任何文件具有mod_mime
可以识别的表示其字符集、内容类型、语言和编码的后缀,那么其结果将取决于MultiViewsMatch
指令的设置,这个指令决定了在MultiViews协商中将使用的处理器、过滤器和其他后缀类型。
Apache从一个类型表或者某个目录的文件名中得到对一个资源的变种列表以后,会使用两种方法之一选择可能的“最佳”变种返回给客户。使用Apache的内容协商功能并不必须了解协商的过程细节,以下对这些方法加以说明,有兴趣的人可以看看。
协商的两种方法:
变元 | 说明 |
---|---|
媒体类型 | 浏览器在Accept头中指明首选项,其中各项与品质因子关联,变种描述也可以有品质因子(参数 "qs")。 |
语言 | 浏览器在Accept-Language头中指明首选项,其中各项与品质因子关联,变种可以与一个或多个语言关联。 |
编码 | 浏览器在Accept-Encoding头中指明首选项,其中各项与品质因子关联。 |
字符集 | 浏览器在Accept-Charset头中指明首选项,其中各项与品质因子关联,变种可以指定一个字符集作为媒体类型的一个参数。 |
Apache使用下述算法选择可能的“最佳”变种返回给浏览器。此算法不能被再配置。其过程如下:
LanguagePriority
指令中的语言顺序(如果有的话)选择最匹配的语言。text/*
媒体类型而没有明确地与一个特定字符集关联的变种,将使用ISO-8859-1。Apache有时会改变按照Apache协商算法应该被严格解析的品质值,从而在浏览器没有发送完整的精确的信息时获得更好的效果。有些很常用的浏览器在许多情况下,会发送导致变种选择错误的Accept头信息。如果一个浏览器发送了完整的且正确的信息,则不会有打乱操作。
Accept:请求头指明了媒体类型的首选项,也可以包含“通配”媒体类型,如"image/*"和匹配任何字串的"*/*"。所以,如果一个请求包含:
Accept: image/*, */*
会指明可以接受任何以"image/"开头的类型,和其他任何类型(因而前面的"image/*"就是多余的)。有些浏览器就会这样例行公事地在明确指定允许的类型后面附加通配类型,比如:
Accept: text/html, text/plain, image/gif, image/jpeg, */*
其目的是表明,明确列出的是首选项,其他不同的表现也可以。这种用法不是不可以,但是*/*其实可以通配所有其他类型,所以不推荐这样用,而应该对*.*赋予一个较低的品质(首选)值0.01,如:
Accept: text/html, text/plain, image/gif, image/jpeg, */*; q=0.01
明确指定的类型没有品质值,所以其品质值是默认的最高值1.0,而*/*是较低的0.01,所以,只有在没有匹配明确指定类型的变种时,才会返回其他类型。
如果Accept:头没有指定任何q因子,Apache设置"*/*"的q值为0.01来模拟上述推荐的行为,还会设置"type/*"的q值为0.02,使之优先于"*/*"。如果Accept:头中任何媒体类型指定了q因子,则不会使用这些特殊值,以使正确发送信息的浏览器能正常运作。
在Apache 2.0中的协商算法中,新增了一些例外的规则,以允许在语言协商匹配失败的情况下,作巧妙的妥协。
通常,当客户端向服务器请求一个不能与浏览器Accept-Language所匹配的唯一的页面时,服务器会返回一个"No Acceptable Variant"或者"Multiple Choices"响应。但是,有可能通过配置Apache,忽略这些情况下的Accept-Language,而返回一个不是非常匹配客户请求的文本,以避免这些错误信息的出现。ForceLanguagePriority
指令可以屏蔽这两种错误信息,并接管由LanguagePriority
指令控制的服务器裁定机制。
服务器还会在匹配失败时尝试用语言子集来匹配。例如,如果一个客户请求了一个语言是en-GB
的英国英语的页面,而服务器只支持HTTP/1.1标准的简单的en
。(注意,在Accept-Language头中指定en-GB
而不是en
,几乎绝对是个错误,因为它似乎暗示阅读的人懂英国英语却不懂大众英语。而不幸的是,许多流行的客户端的默认配置却是这样的)。如果没有可以匹配的语言,服务器将会忽略其语言子集的设定,返回"No Acceptable Variants"错误,或者按LanguagePriority
作妥协。Apache会隐含地在客户可接受语言的列表中附加一个具有很低品质值的父语言,但是,如果客户请求"en-GB; qs=0.9, fr; qs=0.8",那么将返回"fr"的文本,这对遵循HTTP/1.1标准以使正确配置的浏览器能正常工作是必须的。
Apache在变种列表中使用了一个新的{encoding ..}
元素来标记变种,从而扩展了透明内容协商协议(RFC 2295)。实现RVSA/1.0算法(RFC 2296)的目的是识别列表中被编码的变种,作为可以被Accept-Encoding请求头接受的候选变种。在选择最佳变种之前,RVSA/1.0的实现不会对品质因子作四舍五入的运算。
如果使用语言协商,由于文件可以有不止一个的后缀,因此就可以选择不同的名称转换,其后缀顺序通常是不相关的(参见mod_mime文档)。
一个典型的有MIME类型后缀的文件,其后缀可以是编码后缀(如,gz
),当然也可以是不同语言变种后缀(如, en
)。
例:
文件名和有效及无效超链的例子:
文件名 | 有效超链 | 无效超链 |
---|---|---|
foo.html.en | foo foo.html |
- |
foo.en.html | foo | foo.html |
foo.html.en.gz | foo foo.html |
foo.gz foo.html.gz |
foo.en.html.gz | foo | foo.html foo.html.gz foo.gz |
foo.gz.html.en | foo foo.gz foo.gz.html |
foo.html |
foo.html.gz.en | foo foo.html foo.html.gz |
foo.gz |
可以看出,上表中使用没有任何后缀的超链(如, foo
)总是可行的,其优点是,可以隐藏rsp.文件的真实类型,而可以在将来作更改,比如,不用修改超链本身,而改变html
为shtml
或cgi
。
如果希望在超链中继续使用MIME类型(如foo.html
),则语言后缀(如果有,还包括一个编码后缀)必须出现在MIME类型后缀的右边(如, foo.html.en
)。
如果缓存中有一个与请求URL关联的表现,下一次请求该URL时,缓存则可以使用之。但是,如果这个资源在服务器端是可协商的,则可能只有第一次请求的变种是正确的,其后在缓存中命中而取出的结果是错误的。为避免这种情况发生,Apache通常把内容协商之后返回的响应标记为HTTP/1.0客户端不可缓冲。另外Apache还支持HTTP/1.1协议功能以允许缓冲已协商的请求。
对来自遵循HTTP/1.0规范客户端的请求(浏览器或缓存),CacheNegotiatedDocs
指令可以允许缓冲服从协商的请求。此指令应该出现在服务器或虚拟主机的配置中,没有参数,并且对来自HTTP/1.1客户端的请求没有影响。
更多有关内容协商的信息,可以参见Alan J. Flavell's的Language Negotiation Notes,但是注意,此文档可能没有升级以包含Apache 2.0中的改变。