Content Negotiation

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

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

关于内容协商

资源可能有几种不同的表示形式。例如,它可能以不同的语言或不同的媒体类型或它们的组合提供。选择最合适的选择的一种方法是为用户提供索引页,然后让他们选择。但是,服务器通常可以自动选择。之所以可行,是因为浏览器可以在每个请求中发送有关他们偏爱哪种表示形式的信息。例如,浏览器可能指示希望以法语查看信息,如果可能,则以英语显示。浏览器通过请求中的 Headers 指示其首选项。要仅请求法语表示,浏览器将发送

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请求 Headers。 httpd 还支持“透明”内容协商,这是 RFC 2295 和 RFC 2296 中定义的实验性协商协议。它不提供对这些 RFC 中定义的“功能协商”的支持。

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

httpd 协商

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

使用类型图文件

类型 Map 是一个与名为type-map的处理程序相关联的文档(或与旧版 httpd 配置的MIME-type application/x-type-map具有向后兼容性)。请注意,要使用此功能,必须在配置中设置一个处理程序,该处理程序将文件后缀定义为type-map;最好用

AddHandler type-map .var

在服务器配置文件中。

类型 Map 文件应与它们描述的资源具有相同的名称,后跟 extensions.var。在下面显示的示例中,资源名为foo,因此类型 Map 文件名为foo.var

该文件应为每个可用变体都有一个条目。这些条目由连续的 HTTP 格式的标题行组成。不同变体的条目由空白行分隔。空行在条目中是非法的。通常,以一个用于整个合并实体的条目开始一个 Map 文件(尽管这不是必需的,并且如果存在则将被忽略)。下面显示了一个示例 Map 文件。

该文件中的 URI 相对于类型 Map 文件的位置。通常,这些文件与类型 Map 文件位于同一目录中,但这不是必需的。您可以为与 Map 文件位于同一服务器上的任何文件提供绝对或相对 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

还要注意,即使打开“多视图”,类型 Map 文件也将优先于文件名的 extensions。如果变体具有不同的源质量,则可以通过媒体类型的“ 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”参数值的变体的 qs 系数为 1.0. qs 参数指示此变量与其他可用变量相比的相对“质量”,而与 Client 端的功能无关。例如,如果 JPEG 文件试图表示照片,则其源质量通常比 ASCII 文件高。但是,如果要表示的资源是原始 ASCII Literals,则 ASCII 表示的源质量要比 JPEG 表示的源质量高。因此,qs 值特定于给定的变量,具体取决于它代表的资源的性质。

mod_negotiation typemap文档中提供了已识别的 Headers 的完整列表。

Multiviews

MultiViews是每个目录的选项,这意味着可以在httpd.conf<Directory><Location><Files>部分或.htaccess文件中(如果正确设置了AllowOverride)中使用Options伪指令进行设置。注意Options All没有设置MultiViews;您必须按名称要求。

MultiViews的作用如下:如果服务器收到对/some/dir/foo的请求,如果/some/dir启用了MultiViews,并且/some/dir/foo不存在,那么服务器读取目录以查找名为 foo.*的文件,并有效地伪造 type map,它会为所有这些文件命名,并为它们分配相同的媒体类型和内容编码(如果 Client 端按名称请求其中之一)。然后,它选择最符合 Client 要求的匹配项。

如果服务器正在尝试为目录构建索引,则MultiViews还可用于搜索由DirectoryIndex指令命名的文件。如果配置文件指定

DirectoryIndex index

那么服务器将在index.htmlindex.html3之间进行仲裁(如果两者均存在)。如果都不存在,并且index.cgi存在,则服务器将运行它。

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

协商方法

httpd 从类型 Map 文件或目录中的文件名获取给定资源的变体列表之后,它将调用两种方法之一来确定要返回的“最佳”变体(如果有)。为了使用 httpd 的内容协商功能,不必知道实际上如何进行协商的任何细节。但是,本文的其余部分将为感兴趣的人介绍所使用的方法。

有两种协商方法:

Negotiation 维度

Dimension Notes
Media Type 浏览器使用AcceptHeaders 字段指示首选项。每个项目都可以具有关联的品质因数。变体描述也可以具有品质因数(“ qs”参数)。
Language 浏览器使用Accept-LanguageHeaders 字段指示首选项。每个项目都可以有一个品质因数。变体可以与一种,一种或多种语言关联。
Encoding 浏览器使用Accept-EncodingHeaders 字段指示首选项。每个项目都可以有一个品质因数。
Charset 浏览器使用Accept-CharsetHeaders 字段指示首选项。每个项目都可以有一个品质因数。变体可以将字符集指示为媒体类型的参数。

httpd 协商算法

httpd 可以使用以下算法来选择“最佳”变体(如果有)以返回到浏览器。该算法无法进一步配置。其操作如下:

摆弄质量价值观

httpd 有时会通过严格解释上述 httpd 协商算法所期望的值来更改质量值。对于未发送完整或准确信息的浏览器,这是从算法中获得更好的结果。一些最受欢迎的浏览器会发送AcceptHeaders 信息,否则,在许多情况下,这些信息会导致选择错误的变体。如果浏览器发送了完整且正确的信息,这些提琴将不会被应用。

媒体类型和通配符

Accept:请求 Headers 指示媒体类型的首选项。它还可以包括“通配符”媒体类型,例如“ /”匹配任何字符串的“ image/*”或“ /”。因此,请求包括:

Accept: image/*, */*

表示以“ image /”开头的任何类型都可以接受,其他任何类型也可以。一些浏览器除了可以处理的显式类型外,还会例行发送通配符。例如:

Accept: text/html, text/plain, image/gif, image/jpeg, */*

这样做的目的是表明首选显式列出的类型,但是如果可以使用其他表示形式,也可以。使用明确的质量值,浏览器 true 想要的是:

Accept: text/html, text/plain, image/gif, image/jpeg, */*; q=0.01

显式类型没有品质因数,因此它们的默认优先级为 1.0(最高)。通配符*/*的优先级较低,为 0.01,因此,如果没有任何变量与明确列出的类型匹配,则仅返回其他类型。

如果Accept:Headers 根本不包含 q 因子,则 httpd 将 q /的“ /”值(如果存在)设置为 0.01,以模拟所需的行为。还将“类型/ ”格式的通配符的 q 值设置为 0.02(因此,与“ /”匹配时,首选使用通配符。如果Accept:Headers 上的任何媒体类型包含 aq 因子,则这些特殊值均为 not *已应用,因此来自浏览器的请求发送了明确的信息以按预期开始工作。

语言协商 exception

httpd 2.0 中的新功能,已将一些 exception 添加到协商算法中,以便在语言协商无法找到匹配项时允许适当的回退。

当 Client 端请求服务器上的页面,但服务器找不到与浏览器发送的Accept-language相匹配的页面时,服务器将向 Client 端返回“ No Acceptable Variant”或“ Multiple Choices”响应。为避免这些错误消息,可以在这种情况下将 httpd 配置为忽略Accept-language并提供与 Client 端的请求不明确匹配的文档。 ForceLanguagePriority指令可用于覆盖这些错误消息中的一个或两个,并以LanguagePriority指令的形式替换服务器的判断。

如果找不到其他匹配项,服务器还将尝试匹配语言子集。例如,如果 Client 端请求使用en-GB语言表示英式英语的文档,则 HTTP/1.1 标准通常不允许服务器将其与仅标记为en的文档进行匹配。 (请注意,几乎肯定会在Accept-LanguageHeaders 中包含en-GB而不是en,这是配置错误,因为 Reader 不太可能会理解英式英语,但通常不会理解英语.不幸的是,许多当前的 Client 端具有默认配置与此类似.)但是,如果没有其他语言匹配是可能的,并且服务器将返回“ No Acceptable Variants”错误或回退到LanguagePriority,则服务器将忽略子集规范,并将en-GBen文档进行匹配。隐式地,httpd 会将父语言以非常低的质量值添加到 Client 可接受的语言列表中。但是请注意,如果 Client 端请求“ en-GB; q = 0.9,fr; q = 0.8”,并且服务器具有指定为“ en”和“ fr”的文档,则将返回“ fr”文档。这对于保持符合 HTTP/1.1 规范并与正确配置的 Client 端有效地工作是必需的。

为了支持高级技术(例如 Cookie 或特殊的 URL 路径)来确定用户的首选语言,因为 httpd 2.0.47 mod_negotiation识别environment variable prefer-language。如果它存在并且包含适当的语言标签,则mod_negotiation将尝试选择匹配的变体。如果没有此类变体,则适用正常的协商过程。

Example

SetEnvIf Cookie "language=(.+)" prefer-language=$1
Header append Vary cookie

透明内容协商的扩展

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

有关超链接和命名约定的 Comments

如果使用语言协商,则可以在不同的命名约定之间进行选择,因为文件可以具有多个 extensions,并且 extensions 的 Sequences 通常是无关紧要的(有关详细信息,请参见mod_mime文档)。

典型的文件具有 MIME 类型的 extensions(* eg html),也许是编码 extensions( eg gz),当然还有语言 extensions( eg *,en),如果我们对此有不同的语言变体文件。

Examples:

以下是一些文件名示例以及有效和无效的超链接:

Filename Valid hyperlink Invalid hyperlink
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)中使用不带任何 extensions 的名称。好处是您可以隐藏 rsp 文档的实际类型。文件,以后可以将其* e * g *从html更改为shtmlcgi,而无需更改任何超链接引用。

如果要 continue 在超链接(* eg * foo.html)中使用 MIME 类型,则语言 extensions(如果有编码 extensions,则包括编码 extensions)必须位于 MIME 类型 extensions(* eg * ,foo.html.en)。

关于缓存的注意事项

缓存存储表示形式时,会将其与请求 URL 关联。下次请求 URL 时,缓存可以使用存储的表示形式。但是,如果资源在服务器上可协商,则可能导致仅第一个请求的变体被缓存,并且随后的缓存命中可能返回错误的响应。为防止这种情况,httpd 通常将内容协商后返回的所有响应标记为 HTTP/1.0 Client 端不可缓存。 httpd 还支持 HTTP/1.1 协议功能,以允许缓存协商的响应。

对于来自 HTTP/1.0 兼容 Client 端(浏览器或缓存)的请求,指令CacheNegotiatedDocs可用于缓存要协商的响应。该指令可以在服务器配置或虚拟主机中给出,并且不带参数。它对来自 HTTP/1.1 Client 端的请求没有影响。

对于 HTTP/1.1 Client 端,httpd 发送Vary HTTP 响应 Headers 以指示响应的协商维度。缓存可以使用此信息来确定是否可以从本地副本满足后续请求。为了鼓励缓存使用本地副本而不管协商范围如何,请设置force-no-vary environment variable

首页