<-
Apache > HTTP Server > 文件 > Version 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 Server 一樣的 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
內容類型:text/html
內容語言:en

URI:foo.fr.de.html
內容類型:text/html;字符集=iso-8859-2
內容語言:法文、德文

另外請注意,類型地圖檔優先於檔名副檔名,即使在開啟 Multiviews 的情況下也是如此。如果這些變體有不同的來源品質,則可以使用媒體類型的「qs」參數來指出,例如此圖示 (可使用 JPEG、GIF 或 ASCII 藝術格式)

URI:foo

URI:foo.jpeg
內容類型:image/jpeg;qs=0.8

URI:foo.gif
內容類型:image/gif;qs=0.5

URI:foo.txt
內容類型:text/plain;qs=0.01

qs 值範圍為 0.000 至 1.000。請注意,任何 qs 值為 0.000 的變體永遠不會被選取。沒有「qs」參數值的變體會得到 qs 因子 1.0。qs 參數指出此變體相對於其他可用變體的相對「品質」,與客戶端的裝置無關。例如,如果 JPEG 檔案嘗試呈現照片,通常會比 ASCII 檔案有更高的來源品質。然而,如果所呈現的資源是原始的 ASCII 藝術,則 ASCII 呈現會比 JPEG 呈現有更高的來源品質。因此,qs 值會根據它所呈現的資源的性質而特定於給定的變體。

可在 mod_negotiation typemap 文件中取得可辨識的標頭的完整清單。

Multiviews

MultiViews 是一個專屬目錄的選項,表示可以在 httpd.confOptions 指令中的 <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 協商。

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. 選取編碼最佳的變異元素。若有 user-agent 可接受之編碼的變異元素,則僅選取這些變異元素。否則,若編碼和未編碼變異元素混合,則僅選取未編碼變異元素。若所有變異元素皆已編碼或皆未編碼,則選取所有變異元素。
    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-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
top

透明內容協商的延伸

httpd 將透明內容協商協定 (RFC 2295) 延伸如下。使用新的 {編碼 ..} 元素在變異清單中,將僅具有特定內容編碼的變異標示出來。針對 RVSA/1.0 演算法 (RFC 2296) 的實作延伸為,識別清單中的已編碼變異,並在可接受其編碼(根據「接受編碼」請求標頭)時,將這些變異當作候選變異。RVSA/1.0 實作不會在選出最佳變異前,將計算出的品質因子四捨五入為五個小數位。

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)。優點是您可以隱藏文件及檔案的實際類型,並可以在稍後變更其類型,例如,從 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 伺服器相關問題,應導向我們的 IRC 頻道,#httpd(於 Libera.chat)或傳送至我們的 寄件清單