Apache HTTP 服务器版本 2.4
Apache HTTPD 支持内容协商,如 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
httpd 支持 HTTP/1.1 规范中定义的“服务器驱动”内容协商。它完全支持 Accept
、Accept-Language
、Accept-Charset
和 Accept-Encoding
请求标头。httpd 还支持“透明”内容协商,这是一种在 RFC 2295 和 RFC 2296 中定义的实验性协商协议。它不支持这些 RFC 中定义的“功能协商”。
资源是由 URI(RFC 2396)标识的概念实体。像 Apache HTTP 服务器这样的 HTTP 服务器提供对其命名空间内资源的表示形式的访问,每个表示形式都以具有定义的媒体类型、字符集、编码等的字节序列的形式。每个资源在任何给定时间都可能与零个、一个或多个表示形式相关联。如果有多个表示形式可用,则该资源被称为可协商的,其每个表示形式被称为变体。可协商资源的变体变化的方式被称为协商的维度。
为了协商资源,服务器需要获得有关每个变体的信息。这可以通过两种方式之一完成
*.var
文件),该文件明确命名包含变体的文件,或者类型映射是一个与名为 type-map
的处理程序关联的文档(或者,为了与旧的 httpd 配置向后兼容,是 MIME 类型 application/x-type-map
)。请注意,要使用此功能,您必须在配置中设置一个处理程序,该处理程序将文件后缀定义为 type-map
;这最好通过以下方式完成
AddHandler type-map .var
在服务器配置文件中。
类型映射文件应与它们描述的资源具有相同的名称,后跟扩展名 .var
。在下面显示的示例中,资源名为 foo
,因此类型映射文件名为 foo.var
。
此文件应包含每个可用变体的条目;这些条目由连续的 HTTP 格式标头行组成。不同变体的条目用空行分隔。条目内不允许出现空行。通常,映射文件以整个组合实体的条目开头(尽管这不是必需的,如果存在将被忽略)。下面显示了一个示例映射文件。
此文件中的 URI 相对于类型映射文件的位置。通常,这些文件将位于与类型映射文件相同的目录中,但这不是必需的。您可以为与映射文件位于同一服务器上的任何文件提供绝对或相对 URI。
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 处于打开状态。如果变体具有不同的源质量,则可以通过媒体类型的“qs”参数来指示,如以下图片(以 JPEG、GIF 或 ASCII 艺术形式提供)
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 的范围内变化。请注意,任何 qs 值为 0.000 的变体永远不会被选中。没有“qs”参数值的变体将被赋予 1.0 的 qs 因子。qs 参数指示此变体相对于其他可用变体的相对“质量”,独立于客户端的功能。例如,如果 JPEG 文件试图表示一张照片,那么它通常比 ASCII 文件具有更高的源质量。但是,如果表示的资源是原始 ASCII 艺术,那么 ASCII 表示将比 JPEG 表示具有更高的源质量。因此,qs 值特定于给定变体,具体取决于它所表示的资源的性质。
可以在 mod_negotiation 类型映射 文档中找到识别的标头完整列表。
MultiViews
是一个每个目录选项,这意味着它可以使用 httpd.conf
中的 Options
指令在 <Directory>
、<Location>
或 <Files>
部分中设置,或者(如果 AllowOverride
设置正确)在 .htaccess
文件中设置。请注意,Options All
不会设置 MultiViews
;您必须按名称请求它。
MultiViews
的效果如下:如果服务器收到对 /some/dir/foo
的请求,如果 /some/dir
启用了 MultiViews
,并且 /some/dir/foo
不存在,那么服务器将读取目录以查找名为 foo.* 的文件,并有效地伪造一个命名所有这些文件的类型映射,为它们分配与客户端请求其中一个文件时相同的媒体类型和内容编码。然后它选择最符合客户端要求的匹配项。
MultiViews
也可能适用于搜索由 DirectoryIndex
指令命名的文件,如果服务器试图索引目录。如果配置文件指定
DirectoryIndex index
那么服务器将在 index.html
和 index.html3
之间进行仲裁,如果两者都存在。如果两者都不存在,并且 index.cgi
存在,服务器将运行它。
如果在读取目录时找到的文件之一没有 mod_mime
识别的扩展名来指定其字符集、内容类型、语言或编码,那么结果将取决于 MultiViewsMatch
指令的设置。此指令确定处理程序、过滤器和其他扩展类型是否可以参与 MultiViews 协商。
在 httpd 获得给定资源的变体列表后,无论是来自类型映射文件还是来自目录中的文件名,它都会调用两种方法之一来决定要返回的“最佳”变体(如果有)。为了使用 httpd 的内容协商功能,没有必要了解协商实际发生的任何细节。但是,本文档的其余部分将解释对那些感兴趣的人使用的这些方法。
有两种协商方法
维度 | 说明 |
---|---|
媒体类型 | 浏览器通过Accept 头字段指示其偏好。每个项目可以有一个关联的质量因子。变体描述也可以有一个质量因子(“qs”参数)。 |
语言 | 浏览器通过Accept-Language 头字段指示其偏好。每个项目可以有一个关联的质量因子。变体可以与零个、一个或多个语言相关联。 |
编码 | 浏览器通过Accept-Encoding 头字段指示其偏好。每个项目可以有一个关联的质量因子。 |
字符集 | 浏览器通过Accept-Charset 头字段指示其偏好。每个项目可以有一个关联的质量因子。变体可以将字符集作为媒体类型的参数来指示。 |
httpd可以使用以下算法来选择要返回给浏览器的“最佳”变体(如果有)。此算法不可进一步配置。它的工作原理如下
Accept
头字段中的质量因子乘以此变体媒体类型的源质量因子,并选择具有最高值的变体。Accept-Language
头字段中的语言顺序(如果存在),或者使用LanguagePriority
指令中的语言顺序(如果存在)。Accept-Charset
头行中所述。字符集ISO-8859-1是可接受的,除非明确排除。具有text/*
媒体类型但未明确与特定字符集关联的变体被假定为使用ISO-8859-1。Vary
被设置为指示协商的维度(浏览器和缓存可以在缓存资源时使用此信息)。结束。Vary
头字段以指示差异的维度。httpd有时会更改质量值,使其与上述 httpd 协商算法的严格解释所期望的值不同。这样做是为了从算法中获得更好的结果,适用于那些不发送完整或准确信息的浏览器。一些最流行的浏览器发送的Accept
头字段信息,在许多情况下会导致选择错误的变体。如果浏览器发送完整且正确的信息,则不会应用这些调整。
Accept:
请求头指示对媒体类型的偏好。它还可以包含“通配符”媒体类型,例如“image/*”或“*/*”,其中 * 匹配任何字符串。因此,包含以下内容的请求
Accept: image/*, */*
将指示任何以“image/”开头的类型都是可接受的,其他任何类型也是可接受的。一些浏览器除了可以处理的显式类型外,还会定期发送通配符。例如
Accept: text/html, text/plain, image/gif, image/jpeg, */*
这样做是为了指示显式列出的类型是首选的,但如果存在其他表示,那也是可以的。使用显式质量值,浏览器真正想要的是类似以下内容的东西
Accept: text/html, text/plain, image/gif, image/jpeg, */*; q=0.01
显式类型没有质量因子,因此它们默认为 1.0 的偏好(最高)。通配符 */* 被赋予 0.01 的低偏好,因此其他类型只有在没有变体匹配显式列出的类型时才会被返回。
如果Accept:
头字段根本不包含任何 q 因子,httpd 会将 */* 的 q 值(如果存在)设置为 0.01 以模拟所需的行为。它还会将格式为“type/*”的通配符的 q 值设置为 0.02(因此这些通配符比与“*/*”匹配的通配符更优先)。如果Accept:
头字段上的任何媒体类型包含 q 因子,则不会应用这些特殊值,因此来自最初发送显式信息的浏览器的请求按预期工作。
httpd 2.0 中新增了一些协商算法的异常,允许在语言协商无法找到匹配项时优雅地回退。
当客户端请求服务器上的页面,但服务器找不到与浏览器发送的Accept-language
匹配的单个页面时,服务器将向客户端返回“没有可接受的变体”或“多个选择”响应。为了避免这些错误消息,可以配置 httpd 在这些情况下忽略Accept-language
,并提供一个没有明确匹配客户端请求的文档。可以使用ForceLanguagePriority
指令来覆盖这两个错误消息中的一个或两个,并以LanguagePriority
指令的形式替换服务器的判断。
服务器还将尝试匹配语言子集,如果找不到其他匹配项。例如,如果客户端请求使用en-GB
(英国英语)语言的文档,服务器通常不允许根据 HTTP/1.1 标准将其与标记为仅为en
的文档匹配。(请注意,在Accept-Language
头字段中包含en-GB
而不包含en
几乎肯定是一个配置错误,因为读者不太可能理解英国英语,但不能理解英语。不幸的是,许多当前客户端的默认配置类似于此。)但是,如果没有其他语言匹配,并且服务器即将返回“没有可接受的变体”错误或回退到LanguagePriority
,服务器将忽略子集规范并将en-GB
与en
文档匹配。隐式地,httpd 将以非常低的质量值将父语言添加到客户端的可接受语言列表中。但请注意,如果客户端请求“en-GB; q=0.9, fr; q=0.8”,并且服务器具有指定为“en”和“fr”的文档,则将返回“fr”文档。这是为了保持与 HTTP/1.1 规范的兼容性,并与正确配置的客户端有效地协同工作。
为了支持高级技术(例如 cookie 或特殊 URL 路径)来确定用户的首选语言,从 httpd 2.0.47 开始,mod_negotiation
识别环境变量prefer-language
。如果它存在并且包含适当的语言标签,mod_negotiation
将尝试选择匹配的变体。如果没有这样的变体,则应用正常的协商过程。
SetEnvIf Cookie "language=(.+)" prefer-language=$1 Header append Vary cookie
httpd 扩展了透明内容协商协议 (RFC 2295),如下所示。变体列表中使用了一个新的{encoding ..}
元素来标记仅使用特定内容编码可用的变体。RVSA/1.0 算法 (RFC 2296) 的实现扩展为识别列表中的编码变体,并在其编码根据Accept-Encoding
请求头可接受时,将它们用作候选变体。RVSA/1.0 实现不会在选择最佳变体之前将计算出的质量因子四舍五入到 5 位小数。
如果您使用语言协商,您可以选择不同的命名约定,因为文件可以有多个扩展名,并且扩展名的顺序通常无关紧要(有关详细信息,请参阅mod_mime 文档)。
一个典型文件有一个 MIME 类型扩展名(例如,html
),可能有一个编码扩展名(例如,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 时,缓存可以使用存储的表示。但是,如果资源在服务器上可协商,这可能会导致仅缓存第一个请求的变体,并且后续的缓存命中可能会返回错误的响应。为了防止这种情况,httpd 通常会将所有在内容协商后返回的响应标记为 HTTP/1.0 客户端不可缓存。httpd 还支持 HTTP/1.1 协议功能,以允许缓存协商的响应。
对于来自 HTTP/1.0 兼容客户端(浏览器或缓存)的请求,可以使用CacheNegotiatedDocs
指令来允许缓存经过协商的响应。此指令可以在服务器配置或虚拟主机中给出,并且不接受任何参数。它对来自 HTTP/1.1 客户端的请求没有影响。
对于 HTTP/1.1 客户端,httpd 发送一个Vary
HTTP 响应头字段以指示响应的协商维度。缓存可以使用此信息来确定后续请求是否可以从本地副本中提供服务。为了鼓励缓存使用本地副本,无论协商维度如何,请设置force-no-vary
环境变量。