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