<-
Apache > HTTP 服务器 > 文档 > 版本 2.4

内容协商

可用语言:  en  |  fr  |  ja  |  ko  |  tr 

Apache HTTPD 支持内容协商,如 HTTP/1.1 规范中所述。它可以根据浏览器提供的媒体类型、语言、字符集和编码首选项选择资源的最佳表示形式。它还实现了一些功能,以更智能地处理来自发送不完整协商信息的浏览器的请求。

内容协商由 mod_negotiation 模块提供,该模块默认情况下已编译。

Support Apache!

另请参阅

top

关于内容协商

资源可能以多种不同的表示形式提供。例如,它可能以不同的语言或不同的媒体类型提供,或者两者兼而有之。选择最合适的选择的一种方法是为用户提供一个索引页面,让他们选择。但是,服务器通常可以自动选择。这是因为浏览器可以在每个请求中发送有关它们首选的表示形式的信息。例如,浏览器可以指示它希望看到法语信息,如果可能的话,否则英语也可以。浏览器通过请求中的标头指示其首选项。要仅请求法语表示形式,浏览器将发送

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 规范中定义的“服务器驱动”内容协商。它完全支持 AcceptAccept-LanguageAccept-CharsetAccept-Encoding 请求标头。httpd 还支持“透明”内容协商,这是一种在 RFC 2295 和 RFC 2296 中定义的实验性协商协议。它不支持这些 RFC 中定义的“功能协商”。

资源是由 URI(RFC 2396)标识的概念实体。像 Apache HTTP 服务器这样的 HTTP 服务器提供对其命名空间内资源表示形式的访问,每个表示形式都以具有定义的媒体类型、字符集、编码等的字节序列的形式。每个资源在任何给定时间都可能与零个、一个或多个表示形式相关联。如果有多个表示形式可用,则该资源被称为可协商的,其每个表示形式被称为变体。可协商资源的变体变化的方式被称为协商的维度

top

httpd 中的协商

为了协商资源,服务器需要获得有关每个变体的信息。这可以通过两种方式之一完成

使用类型映射文件

类型映射是一个与名为 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

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.htmlindex.html3 之间进行仲裁,如果两者都存在。如果两者都不存在,并且 index.cgi 存在,服务器将运行它。

如果在读取目录时找到的文件之一没有 mod_mime 识别的扩展名来指定其字符集、内容类型、语言或编码,那么结果将取决于 MultiViewsMatch 指令的设置。此指令确定处理程序、过滤器和其他扩展类型是否可以参与 MultiViews 协商。

top

协商方法

在 httpd 获得给定资源的变体列表后,无论是来自类型映射文件还是来自目录中的文件名,它都会调用两种方法之一来决定要返回的“最佳”变体(如果有)。为了使用 httpd 的内容协商功能,没有必要了解协商实际发生的任何细节。但是,本文档的其余部分将解释对那些感兴趣的人使用的这些方法。

有两种协商方法

  1. 使用 httpd 算法的服务器驱动协商在正常情况下使用。httpd 算法将在下面更详细地解释。当使用此算法时,httpd 有时可以“调整”特定维度的质量因子以获得更好的结果。httpd 可以调整质量因子的方式将在下面更详细地解释。
  2. 透明内容协商在浏览器通过 RFC 2295 中定义的机制专门请求此功能时使用。此协商方法使浏览器完全控制决定“最佳”变体,因此结果取决于浏览器使用的特定算法。作为透明协商过程的一部分,浏览器可以要求 httpd 运行 RFC 2296 中定义的“远程变体选择算法”。

协商维度

维度 说明
媒体类型 浏览器通过Accept头字段指示其偏好。每个项目可以有一个关联的质量因子。变体描述也可以有一个质量因子(“qs”参数)。
语言 浏览器通过Accept-Language头字段指示其偏好。每个项目可以有一个关联的质量因子。变体可以与零个、一个或多个语言相关联。
编码 浏览器通过Accept-Encoding头字段指示其偏好。每个项目可以有一个关联的质量因子。
字符集 浏览器通过Accept-Charset头字段指示其偏好。每个项目可以有一个关联的质量因子。变体可以将字符集作为媒体类型的参数来指示。

httpd协商算法

httpd可以使用以下算法来选择要返回给浏览器的“最佳”变体(如果有)。此算法不可进一步配置。它的工作原理如下

  1. 首先,对于协商的每个维度,检查相应的Accept*头字段并为每个变体分配一个质量。如果任何维度的Accept*头字段暗示此变体不可接受,则将其消除。如果没有任何变体保留,则转到步骤 4。
  2. 通过消除过程选择“最佳”变体。以下每个测试按顺序应用。任何在每个测试中未被选中的变体都会被消除。在每个测试之后,如果只保留一个变体,则将其选为最佳匹配并继续执行步骤 3。如果保留多个变体,则继续执行下一个测试。
    1. Accept头字段中的质量因子乘以此变体媒体类型的源质量因子,并选择具有最高值的变体。
    2. 选择具有最高语言质量因子的变体。
    3. 选择具有最佳语言匹配的变体,使用Accept-Language头字段中的语言顺序(如果存在),或者使用LanguagePriority指令中的语言顺序(如果存在)。
    4. 选择具有最高“级别”媒体参数的变体(用于提供text/html媒体类型的版本)。
    5. 选择具有最佳字符集媒体参数的变体,如Accept-Charset头行中所述。字符集ISO-8859-1是可接受的,除非明确排除。具有text/*媒体类型但未明确与特定字符集关联的变体被假定为使用ISO-8859-1。
    6. 选择那些具有与ISO-8859-1相同的关联字符集媒体参数的变体。如果没有这样的变体,则选择所有变体。
    7. 选择具有最佳编码的变体。如果存在对用户代理可接受的编码的变体,则仅选择这些变体。否则,如果存在编码和非编码变体的混合,则仅选择未编码的变体。如果所有变体都被编码或所有变体都没有被编码,则选择所有变体。
    8. 选择具有最小内容长度的变体。
    9. 选择剩余变体中的第一个变体。这将是类型映射文件中列出的第一个变体,或者当从目录中读取变体时,其文件名在使用 ASCII 代码顺序排序时排在最前面的变体。
  3. 算法现在已选择了一个“最佳”变体,因此将其作为响应返回。HTTP 响应头Vary被设置为指示协商的维度(浏览器和缓存可以在缓存资源时使用此信息)。结束。
  4. 到达这里意味着没有选择任何变体(因为没有一个对浏览器可接受)。返回 406 状态(表示“没有可接受的表示”),响应主体包含一个 HTML 文档,其中列出了可用的变体。还设置 HTTP Vary头字段以指示差异的维度。
top

调整质量值

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-GBen文档匹配。隐式地,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
top

对透明内容协商的扩展

httpd 扩展了透明内容协商协议 (RFC 2295),如下所示。变体列表中使用了一个新的{encoding ..}元素来标记仅使用特定内容编码可用的变体。RVSA/1.0 算法 (RFC 2296) 的实现扩展为识别列表中的编码变体,并在其编码根据Accept-Encoding请求头可接受时,将它们用作候选变体。RVSA/1.0 实现不会在选择最佳变体之前将计算出的质量因子四舍五入到 5 位小数。

top

关于超链接和命名约定的说明

如果您使用语言协商,您可以选择不同的命名约定,因为文件可以有多个扩展名,并且扩展名的顺序通常无关紧要(有关详细信息,请参阅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更改为shtmlcgi,而无需更改任何超链接引用。

如果您想在超链接中继续使用 MIME 类型(例如 foo.html),则语言扩展名(如果存在编码扩展名,则包括编码扩展名)必须位于 MIME 类型扩展名的右侧(例如foo.html.en)。

top

关于缓存的说明

当缓存存储表示时,它会将其与请求 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 环境变量

可用语言:  en  |  fr  |  ja  |  ko  |  tr 

top

评论

注意
这不是问答部分。此处放置的评论应指向有关改进文档或服务器的建议,如果它们被实施或被认为无效/主题外,可能会被我们的版主删除。有关如何管理 Apache HTTP Server 的问题应发送到我们的 IRC 频道 #httpd(在 Libera.chat 上),或发送到我们的邮件列表