內容協商
在 HTTP 中,**內容協商**機制用於為同一 URI 的資源提供不同的 表示形式,幫助使用者代理指定最適合使用者的表示形式(例如,哪種文件語言、哪種影像格式或哪種內容編碼)。
注意:您可以在 WHATWG 的維基頁面 上找到 HTTP 內容協商的一些缺點。HTML 透過 <source> 元素 等方式提供了內容協商的替代方案。
內容協商原則
一個特定的文件稱為**資源**。當客戶端想要獲取資源時,客戶端透過 URL 請求它。伺服器使用此 URL 從可用變體中選擇一個(每個變體稱為**表示形式**),並將特定表示形式返回給客戶端。整個資源以及每個表示形式都有一個特定的 URL。**內容協商**確定在呼叫資源時如何選擇特定表示形式。在客戶端和伺服器之間進行協商有幾種方法。
最合適的表示形式透過以下兩種機制之一確定
- 客戶端的特定 HTTP 標頭(**伺服器驅動協商**或**主動協商**),這是協商特定資源型別的標準方法。
- 伺服器的
300(多個選擇)或406(不可接受)、415(不支援的媒體型別)HTTP 響應程式碼(**代理驅動協商**或**被動協商**),用作回退機制。
多年來,其他內容協商提案,如 透明內容協商 和 Alternates 標頭,已被提出。但它們未能獲得關注,最終被放棄。
伺服器驅動的內容協商
在**伺服器驅動內容協商**或**主動內容協商**中,瀏覽器(或任何其他型別的使用者代理)會將幾個 HTTP 標頭與 URL 一起傳送。這些標頭描述了使用者的首選選擇。伺服器將這些標頭用作提示,並透過內部演算法選擇最適合的內容來提供給客戶端。如果它無法提供合適的資源,它可能會響應 406(不可接受)或 415(不支援的媒體型別),並設定它支援的媒體型別的標頭(例如,使用 Accept-Post 或 Accept-Patch 分別用於 POST 和 PATCH 請求)。該演算法是特定於伺服器的,並且沒有在標準中定義。請參閱 Apache 協商演算法。
HTTP/1.1 標準定義了啟動伺服器驅動協商的標準標頭列表(如 Accept、Accept-Encoding 和 Accept-Language)。儘管 User-Agent 不在此列表中,但它有時也用於傳送請求資源的特定表示形式。然而,這並不總是被認為是最佳實踐。伺服器使用 Vary 標頭來指示它實際用於內容協商的標頭(更準確地說,是關聯的請求標頭),以便 快取 可以最佳地工作。
除了這些,還有一個實驗性的提案,即向可用標頭列表中新增更多標頭,稱為**客戶端提示**。客戶端提示會通告使用者代理執行在什麼樣的裝置上(例如,臺式計算機或移動裝置)。
即使伺服器驅動內容協商是就資源的特定表示形式達成一致的最常見方式,它也有一些缺點
- 伺服器不完全瞭解瀏覽器。即使使用客戶端提示擴充套件,它也不能完全瞭解瀏覽器的功能。與客戶端做出選擇的被動內容協商不同,伺服器的選擇總是有點隨意。
- 來自客戶端的資訊非常冗長(HTTP/2 標頭壓縮緩解了這個問題),而且存在隱私風險(HTTP 指紋識別)。
- 由於傳送了給定資源的多個表示形式,共享快取效率較低,伺服器實現也更加複雜。
Accept 標頭
Accept 標頭列出了代理願意處理的媒體資源的 MIME 型別。這是一個用逗號分隔的 MIME 型別列表,每個 MIME 型別都與一個質量因子結合,質量因子表示不同 MIME 型別之間相對偏好程度的引數。
Accept 標頭由瀏覽器或任何其他使用者代理定義,並根據上下文而異。例如,獲取 HTML 頁面或影像、影片或指令碼。當獲取在位址列中輸入的文件或透過 <img>、<video> 或 <audio> 元素連結的元素時,它與獲取文件時的行為不同。瀏覽器可以自由使用它們認為最合適的標頭值;一個關於 常見瀏覽器預設值的詳盡列表 可供參考。
Accept-CH 標頭
注意:這是**實驗性**技術**客戶端提示**的一部分。Chrome 46 或更高版本提供了初始支援。裝置記憶體值在 Chrome 61 或更高版本中可用。
實驗性的 Accept-CH 列出了伺服器可以用來選擇合適響應的配置資料。有效值為
| 值 | 含義 |
|---|---|
Device-Memory |
Device-Memory |
指示裝置 RAM 的近似數量。此值是透過四捨五入到最接近的 2 的冪並將該數字除以 1024 獲得的近似值。例如,512 兆位元組將報告為 |
Viewport-Width |
指示佈局視窗寬度(以 CSS 畫素為單位)。 |
Width |
指示資源寬度(以物理畫素為單位)(換句話說,是影像的固有尺寸)。
Accept-Encoding 標頭
Accept-Encoding 標頭定義了可接受的內容編碼(支援的壓縮)。該值是一個質量因子列表(例如,br, gzip;q=0.8),表示編碼值的優先順序。預設值 identity 具有最低優先順序(除非另有說明)。
壓縮 HTTP 訊息是提高網站效能的最重要方法之一。它縮小了傳輸資料的尺寸,並更有效地利用了可用頻寬。瀏覽器總是傳送此標頭,伺服器應該配置為使用壓縮。
Accept-Language 標頭
Accept-Language 標頭用於指示使用者的語言偏好。它是一個帶有質量因子的值列表(例如,"de, en;q=0.7)。通常會根據使用者代理的圖形介面的語言設定一個預設值,但大多數瀏覽器允許設定不同的語言偏好。
- 由於 基於配置的熵 增加,可以使用修改後的值來識別使用者指紋。不建議更改它,網站不能信任此值來反映使用者的真實意圖。網站設計人員最好避免透過此標頭進行語言檢測,因為它會導致糟糕的使用者體驗。
- 他們應該始終提供一種覆蓋伺服器選擇的語言的方法,例如,在網站上提供語言選單。大多數使用者代理提供一個與使用者介面語言相適應的
Accept-Language標頭的預設值。終端使用者通常不會修改它,因為他們要麼不知道如何修改,要麼無法根據他們的計算環境進行修改。
一旦使用者覆蓋了伺服器選擇的語言,網站就不應該再使用語言檢測,而應該堅持使用明確選擇的語言。換句話說,只有網站的入口頁面應該使用此標頭來選擇合適的語言。
User-Agent 標頭
注意:儘管此標頭有合法用途,可以用來選擇內容,但 依賴它來定義使用者代理支援的功能被認為是不好的做法。
User-Agent 標頭標識傳送請求的瀏覽器。此字串可能包含一個空格分隔的**產品令牌**和**註釋**列表。
**產品令牌**是一個名稱,後面跟著一個 '/' 和一個版本號,如 Firefox/4.0.1。使用者代理可以根據需要包含任意數量的這些令牌。**註釋**是一個可選字串,用括號分隔。註釋中提供的資訊沒有標準化,儘管幾個瀏覽器會向其中新增幾個用 ';' 分隔的令牌。
Vary 響應標頭
與前面的 Accept-* 標頭(由客戶端傳送)形成對比的是,Vary HTTP 標頭由 Web 伺服器在其響應中傳送。它指示伺服器在伺服器驅動內容協商階段使用的標頭列表。Vary 標頭是必要的,因為它可以告知快取決策標準,以便它可以重新建立決策標準。這使得快取可以正常工作,同時確保向用戶提供正確的內容。
Vary 頭部在 HTTP 1.1 版本中被新增,它允許快取正常工作。為了與伺服器驅動的內容協商協同工作,快取需要知道伺服器用來選擇傳輸內容的標準。這樣,快取就能重放演算法,並能夠直接提供可接受的內容,而無需向伺服器發出更多請求。顯然,萬用字元 '*' 會阻止快取發生,因為快取無法知道它背後的元素。有關更多資訊,請參見 HTTP 快取 > 不同響應。
代理驅動協商
伺服器驅動的協商有一些缺點:它不能很好地擴充套件。協商使用每個功能一個頭部。如果你想使用螢幕大小、解析度或其他維度,你需要建立一個新的 HTTP 頭部。然後必須在每次請求中傳送這些頭部。如果頭部很少,這不是問題,但是隨著頭部數量的增加,訊息大小最終會影響效能。頭部發送得越精確,傳送的熵就越多,從而允許更多 HTTP 指紋識別以及相應的隱私問題。
HTTP 允許另一種協商型別:代理驅動的協商或響應式協商。在這種情況下,伺服器會返回一個包含指向可用替代資源連結的頁面,以應對模糊的請求。使用者會看到這些資源,並選擇其中一個使用。
不幸的是,HTTP 標準沒有指定在可用資源之間選擇頁面的格式,這阻止了該流程自動執行。除了回退到伺服器驅動的協商之外,這種方法幾乎總是與指令碼一起使用,尤其是與 JavaScript 重定向一起使用:在檢查協商標準後,指令碼會執行重定向。第二個問題是,需要一個額外的請求才能獲取真實資源,從而減慢了使用者獲得資源的速度。