Apache HTTP Server 版本2.2
Apache支持HTTP/1.1規(guī)范中定義的內(nèi)容協(xié)商,它可以根據(jù)瀏覽器提供的參數(shù)選擇一個(gè)資源最合適的媒體類型、語言、字符集和編碼的表現(xiàn)方式。它還實(shí)現(xiàn)了一些對(duì)瀏覽器發(fā)送不完整內(nèi)容協(xié)商信息進(jìn)行智能處理的能力。
內(nèi)容協(xié)商由mod_negotiation
模塊支持,并被默認(rèn)編譯進(jìn)服務(wù)器。
一個(gè)資源可能會(huì)有多種不同的表現(xiàn)形式,比如,可能會(huì)有不同語言或者媒體類型的版本甚至其組合。最常用的選擇方法是提供一個(gè)索引頁以供選擇。但是由于瀏覽器可以在請(qǐng)求頭信息中提供其首選項(xiàng)的表現(xiàn)形式,因此就有可能讓服務(wù)器進(jìn)行自動(dòng)選擇。比如,瀏覽器可以表明希望看見法語的信息,如果沒有,英語的也行。如需僅請(qǐng)求法語的表現(xiàn)形式,瀏覽器可以發(fā)出:
Accept-Language: fr
注意:此首選項(xiàng)信息僅當(dāng)存在多種可選的語言表現(xiàn)形式時(shí)才有效。
下面是一個(gè)更復(fù)雜的請(qǐng)求,瀏覽器表明,可以接受法語和英語,但最好是法語;接受各種媒體類型,最好是HTML,但純文件或其他文本類型也可以;最好是GIF或JPEG,但其他媒體類型也可以,并允許其他媒體類型作為最終表現(xiàn)形式:
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規(guī)范中定義的"服務(wù)器驅(qū)動(dòng)"的內(nèi)容協(xié)商, 可以完全地支持Accept
、Accept-Language
、Accept-Charset
、Accept-Encoding
請(qǐng)求頭,這些是RFC2295和RFC2296中定義的實(shí)驗(yàn)協(xié)商協(xié)議,但是不支持這些RFC中定義的"功能協(xié)商"。
資源(resource)是一個(gè)在URI(RFC2396)中定義的概念上的實(shí)體。一個(gè)HTTP服務(wù)器,比如Apache,以表現(xiàn)形式(representation)提供對(duì)其名稱空間中資源的訪問,各種表現(xiàn)形式由已定義的媒體類型、字符集和編碼的字節(jié)流構(gòu)成。任何一個(gè)特定的時(shí)刻,一個(gè)資源可以沒有,或者有一個(gè),或者有多個(gè)表現(xiàn)形式。如果有多個(gè)表現(xiàn)形式存在,則稱該資源是可協(xié)商的(negotiable),其各種表現(xiàn)形式稱為變種(variant)。而一個(gè)可協(xié)商的資源的各種變種的區(qū)別途徑稱為變?cè)?dimension)。
可以使用下述兩種途徑之一向服務(wù)器提供有關(guān)各變種的信息,以實(shí)現(xiàn)對(duì)資源的協(xié)商:
*.var
文件)明確指定各變種的文件名。類型表是一個(gè)與type-map
處理器關(guān)聯(lián)的文檔(或者兼容早期Apache配置的MIME類型:application/x-type-map
)。要使用這個(gè)功能,必須在配置中建立處理器,以定義一個(gè)文件后綴為type-map
,最好的方法是在配置文件中這樣設(shè)置:
AddHandler type-map .var
類型表文件應(yīng)該與所描述的資源同名,且對(duì)每個(gè)有效變種都有一個(gè)塊(entry),每個(gè)塊由若干連續(xù)的HTTP頭行組成,不同變種的塊用空行分開,塊中不允許有空行。習(xí)慣上,類型表都以一個(gè)描述總體性質(zhì)的組合塊作為開始(這不是必須的,如果有也會(huì)被忽略)。下例是一個(gè)描述資源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
設(shè)置為 On
,類型表仍然優(yōu)先于文件后綴名。如果不同的變種具有不同的資源品質(zhì),就可以對(duì)媒體類型使用"qs"參數(shù)來表示這種不同。下例演示了一個(gè)圖片的 jpeg, gif, ASCII-art 三個(gè)有效變種:
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的變種永遠(yuǎn)不會(huì)被選擇,沒有指定qs值的變種其qs值為1.0。qs值表示一個(gè)變種相對(duì)于其他變種的"品質(zhì)",比如在表現(xiàn)一張照片時(shí),jpeg通常比字符構(gòu)圖有更高的品質(zhì);而如果要表現(xiàn)的本來就是一個(gè)ASCII-art ,那么當(dāng)然字符構(gòu)圖就會(huì)比jpeg文件有更高的品質(zhì)。因此,qs的值取決于變種所表現(xiàn)的資源本身。
mod_negotation類型表文檔中有完整的HTTP頭的列表。
MultiViews
是一個(gè)針對(duì)每個(gè)目錄的選項(xiàng),也就是說可以在httpd.conf
或.htaccess
(如果正確設(shè)置了AllowOverride
)文件中的<Directory>
、<Location>
、<Files>
配置段中,用Options
指令來指定。注意,Options All
并不會(huì)設(shè)置MultiViews
,你必須明確地指定。
MultiViews
的效果是:如果服務(wù)器收到對(duì)/some/dir/foo
的請(qǐng)求,而/some/dir/foo
并不存在,但是如果/some/dir
啟用了MultiViews
,則服務(wù)器會(huì)查找這個(gè)目錄下所有的foo.* 文件,并有效地偽造一個(gè)說明這些foo.* 文件的類型表,分配給他們相同的媒體類型及內(nèi)容編碼,并選擇其中最合適的匹配返回給客戶。
MultiViews
還可以在服務(wù)器檢索一個(gè)目錄時(shí),用于DirectoryIndex
指令搜索的文件名。如果設(shè)置了:
DirectoryIndex index
而index.html
和index.html3
并存,則服務(wù)器會(huì)作一個(gè)權(quán)衡;如果都沒有,但是有index.cgi
,則服務(wù)器會(huì)執(zhí)行它。
如果一個(gè)目錄中沒有任何文件具有mod_mime
可以識(shí)別的表示其字符集、內(nèi)容類型、語言和編碼的后綴,那么其結(jié)果將取決于MultiViewsMatch
指令的設(shè)置,這個(gè)指令決定了在MultiViews協(xié)商中將使用的處理器、過濾器和其他后綴類型。
Apache從一個(gè)類型表或者某個(gè)目錄的文件名中得到一個(gè)資源變種列表以后,會(huì)使用兩種方法之一選擇可能的"最佳"變種返回給客戶。使用Apache的內(nèi)容協(xié)商功能并不需要了解其細(xì)節(jié),以下文檔對(duì)這些方法加以詳細(xì)說明,供有興趣的人看看。
協(xié)商有兩種方法:
變?cè)?/th> | 說明 |
---|---|
媒體類型 | 瀏覽器在Accept 頭中指明首選項(xiàng),其中各項(xiàng)與品質(zhì)因子關(guān)聯(lián),變種描述也可以有品質(zhì)因子(參數(shù)"qs")。 |
語言 | 瀏覽器在Accept-Language 頭中指明首選項(xiàng),其中各項(xiàng)與品質(zhì)因子關(guān)聯(lián),變種可以與零個(gè)、一個(gè)或多個(gè)語言關(guān)聯(lián)。 |
編碼 | 瀏覽器在Accept-Encoding 頭中指明首選項(xiàng),其中各項(xiàng)與品質(zhì)因子關(guān)聯(lián)。 |
字符集 | 瀏覽器在Accept-Charset 頭中指明首選項(xiàng),其中各項(xiàng)與品質(zhì)因子關(guān)聯(lián),變種可以指定一個(gè)字符集作為媒體類型的一個(gè)參數(shù)。 |
Apache使用下述算法選擇可能的"最佳"變種返回給瀏覽器。此算法不能被再配置。其過程如下:
Accept
頭的品質(zhì)因子乘以該變種媒體類型的還原品質(zhì)因子,選擇乘積最高者。Accept-Language
頭中的語言順序(如果存在的話),或者使用LanguagePriority
指令中的語言順序(如果存在的話)選擇最匹配的語言。Accept-Charset
頭中指定的最佳字符集媒體參數(shù)的變種。如果沒有明確指定,則使用ISO-8859-1字符集。具有text/*
媒體類型而沒有明確地與一個(gè)特定字符集關(guān)聯(lián)的變種,將使用ISO-8859-1。Vary
會(huì)指明協(xié)商的變?cè)?瀏覽器和緩存可以利用此信息緩存該資源)。Vary
中指明了變種的變?cè)?/li>
Apache有時(shí)會(huì)改變按照Apache協(xié)商算法應(yīng)該被嚴(yán)格解析的品質(zhì)值,從而在瀏覽器沒有發(fā)送完整的精確的信息時(shí)獲得更好的效果。有些很常用的瀏覽器在許多情況下,會(huì)發(fā)送導(dǎo)致變種選擇錯(cuò)誤的Accept
頭信息。如果一個(gè)瀏覽器發(fā)送了完整的且正確的信息,則不會(huì)有打亂操作。
Accept:
請(qǐng)求頭指明了媒體類型的首選項(xiàng),也可以包含"通配"媒體類型,如"image/*"和匹配任何字符串的"*/*"。所以,如果一個(gè)請(qǐng)求包含:
Accept: image/*, */*
會(huì)指明可以接受任何以"image/"開頭的類型,和其他任何類型(因而前面的"image/*"就是多余的)。有些瀏覽器就會(huì)這樣例行公事地在明確指定允許的類型后面附加通配類型,比如:
Accept: text/html, text/plain, image/gif, image/jpeg, */*
其目的是表明,明確列出的是首選項(xiàng),其他不同的表現(xiàn)也可以。這種用法不是不可以,但是"*/*"其實(shí)可以通配所有其他類型,所以不推薦這樣用,而應(yīng)該對(duì)"*.*"賦予一個(gè)較低的品質(zhì)(首選)值0.01,如:
Accept: text/html, text/plain, image/gif, image/jpeg, */*; q=0.01
明確指定的類型沒有品質(zhì)值,所以其品質(zhì)值是默認(rèn)的最高值1.0,而"*/*"是較低的0.01,所以,只有在沒有匹配明確指定類型的變種時(shí),才會(huì)返回其他類型。
如果Accept:
頭沒有指定任何q因子,那么Apache設(shè)置"*/*"的q值為0.01來模擬上述推薦的行為,還會(huì)設(shè)置"type/*"的q值為0.02,使之優(yōu)先于"*/*"。如果Accept:
頭中任何媒體類型指定了q因子,則不會(huì)使用這些特殊值,以使正確發(fā)送信息的瀏覽器能正常運(yùn)作。
在Apache 2.0中的協(xié)商算法中,新增了一些例外的規(guī)則,以允許在語言協(xié)商匹配失敗的情況下,作巧妙的妥協(xié)。
通常,當(dāng)客戶端向服務(wù)器請(qǐng)求一個(gè)不能與瀏覽器Accept-language
所匹配的唯一的頁面時(shí),服務(wù)器會(huì)返回一個(gè)"No Acceptable Variant" 或者 "Multiple Choices" 響應(yīng)。但是,有可能通過配置Apache,忽略這些情況下的Accept-language
,而返回一個(gè)不是非常匹配客戶請(qǐng)求的文本,以避免這些錯(cuò)誤信息的出現(xiàn)。ForceLanguagePriority
指令可以屏蔽這兩種錯(cuò)誤信息,并接管由LanguagePriority
指令控制的服務(wù)器裁定機(jī)制。
服務(wù)器還會(huì)在匹配失敗時(shí)嘗試用語言子集來匹配。例如,如果一個(gè)客戶請(qǐng)求了一個(gè)語言是en-GB
的英國英語的頁面,而服務(wù)器只支持HTTP/1.1標(biāo)準(zhǔn)的簡單的en
。(注意,在Accept-Language
中指定en-GB
而不是en
幾乎絕對(duì)是個(gè)錯(cuò)誤,因?yàn)樗坪醢凳鹃喿x的人懂英國英語卻不懂大眾英語。而不幸的是,許多流行的客戶端的默認(rèn)配置卻是這樣的)。如果沒有可以匹配的語言,服務(wù)器將會(huì)忽略其語言子集的設(shè)定,返回"No Acceptable Variants"錯(cuò)誤,或者按LanguagePriority
指令作妥協(xié)。Apache會(huì)隱含地在客戶可接受語言的列表中附加一個(gè)具有很低品質(zhì)值的父語言,但是,如果客戶請(qǐng)求"en-GB; q=0.9, fr; q=0.8" 那么將返回"fr"的文本,這對(duì)遵循HTTP/1.1標(biāo)準(zhǔn)以使正確配置的瀏覽器能正常工作是必須的。
為了支持用于確定用戶首選語言的高級(jí)技術(shù)(比如cookies或特殊的URL路徑),從2.0.47版本起mod_negotiation
模塊開始支持prefer-language
環(huán)境變量。如果存在并且包含一個(gè)語言標(biāo)簽,mod_negotiation
模塊將會(huì)嘗試選擇一個(gè)匹配的變種。如果不存在這樣的變種,將會(huì)使用上述通常的協(xié)商過程。
SetEnvIf Cookie "language=(.+)" prefer-language=$1
Apache在變種列表中使用了一個(gè)新的{encoding ..}
元素來標(biāo)記變種,從而擴(kuò)展了透明內(nèi)容協(xié)商協(xié)議(RFC2295)。實(shí)現(xiàn)RVSA/1.0算法(RFC2296)的目的是識(shí)別列表中被編碼的變種,作為可以被Accept-Encoding
請(qǐng)求頭接受的候選變種。在選擇最佳變種之前,RVSA/1.0的實(shí)現(xiàn)不會(huì)對(duì)品質(zhì)因子作四舍五入的運(yùn)算。
如果使用語言協(xié)商,由于文件可以有不止一個(gè)后綴,因此就可以選擇不同的名稱轉(zhuǎn)換,其后綴順序通常是無關(guān)緊要的(參見mod_mime文檔)。
一個(gè)典型的有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
)總是可行的,其優(yōu)點(diǎn)是可以隱藏rsp. 文件的真實(shí)類型,而可以在將來作更改,比如,不用修改超鏈本身,而改變html
為shtml
或cgi
。
如果希望在超鏈中繼續(xù)使用MIME類型(如foo.html
),則語言后綴(還包括一個(gè)編碼后綴)必須出現(xiàn)在MIME類型后綴的右邊(如foo.html.en
)。
如果緩存中有一個(gè)與特定URL關(guān)聯(lián)的表現(xiàn)形式(representation),那么下一次該URL被請(qǐng)求時(shí),緩存就可以使用它。但是,如果這個(gè)資源在服務(wù)器端是可協(xié)商的,則可能只有第一次請(qǐng)求的變種是正確的,而其后由于緩存中命中而取出的結(jié)果是錯(cuò)誤的。為避免這種情況的發(fā)生,Apache通常把內(nèi)容協(xié)商之后返回的響應(yīng)標(biāo)記為不可以被HTTP/1.1客戶端緩沖。另外Apache還支持HTTP/1.1協(xié)議的功能以允許緩沖已協(xié)商的請(qǐng)求。
對(duì)來自HTTP/1.0客戶端的請(qǐng)求(瀏覽器或緩存),CacheNegotiatedDocs
指令可以允許緩存服從協(xié)商的請(qǐng)求。此指令應(yīng)該出現(xiàn)在主服務(wù)器或虛擬主機(jī)的配置中,沒有參數(shù),并且對(duì)來自HTTP/1.1客戶端的請(qǐng)求沒有影響。
對(duì)于遵守HTTP/1.1規(guī)范的客戶端,Apache發(fā)送一個(gè)Vary
應(yīng)答頭以指定該應(yīng)答的協(xié)商變?cè)。緩存可以使用這個(gè)信息來判斷一個(gè)其后的請(qǐng)求是否可以從本地副本中提供服務(wù)。為了鼓勵(lì)緩存使用本地副本而不是協(xié)商變?cè),?qǐng)?jiān)O(shè)置force-no-vary
環(huán)境變量。
更多有關(guān)內(nèi)容協(xié)商的信息,可以參見Alan J. Flavell的Language Negotiation Notes,但是注意,此文檔可能沒有升級(jí)以包含Apache2.0中的改變。