內容協商
要了解 Vary 的作用,先得了解 HTTP 的內容協商機制。有時候,同一個 URL 可以提供多份不同的文檔,這就要求服務端和客戶端之間有一個選擇最合適版本的機制,這就是內容協商。
協商方式有兩種,一種是服務端把文檔可用版本列表發給客戶端讓用戶選,這可以使用 300 Multiple Choices 狀態碼來實現。這種方案有不少問題,首先多一次網絡往返;其次服務端同一文檔的某些版本可能是為擁有某些技術特征的客戶端準備的,而普通用戶不一定了解這些細節。舉個例子,服務端通常可以將靜態資源輸出為壓縮和未壓縮兩個版本,壓縮版顯然是為支持壓縮的客戶端而準備的,但如果讓普通用戶選,很可能選擇錯誤的版本。
HTTP 的內容協商通常使用另外一種方案:服務端根據客戶端發送的請求頭中某些字段自動發送最合適的版本。可以用于這個機制的請求頭字段又分兩種:內容協商專用字段(Accept 字段)、其他字段。
首先來看 Accept 字段,詳見下表:
請求頭字段 |
說明 |
響應頭字段 |
Accept |
告知服務器發送何種媒體類型 |
Content-Type |
Accept-Language |
Accept-Language |
Content-Language |
Accept-Charset |
告知服務器發送何種字符集 |
Content-Type |
Accept-Encoding |
告知服務器采用何種壓縮方式 |
告知服務器采用何種壓縮方式 |
例如客戶端發送以下請求頭:
Accept:*/* Accept-Encoding:gzip,deflate,sdch Accept-Language:zh-CN,en-US;q=0.8,en;q=0.6
表示它可以接受任何 MIME 類型的資源;支持采用 gzip、deflate 或 sdch 壓縮過的資源;可以接受 zh-CN、en-US 和 en 三種語言,并且 zh-CN 的權重最高(q 取值 0 - 1,最高為 1,最低為 0,默認為 1),服務端應該優先返回語言等于 zh-CN 的版本。
瀏覽器的響應頭可能是這樣的:
Content-Type: text/javascript Content-Encoding: gzip
表示這個文檔確切的 MIME 類型是 text/javascript;文檔內容進行了 gzip 壓縮;響應頭沒有 Content-Language 字段,通常說明返回版本的語言正好是請求頭 Accept-Language 中權重最高的那個。
有時候,上面四個 Accept 字段并不夠用,例如要針對特定瀏覽器如 IE6 輸出不一樣的內容,就需要用到請求頭中的 User-Agent 字段。類似的,請求頭中的 Cookie 也可能被服務端用做輸出差異化內容的依據。
由于客戶端和服務端之間可能存在一個或多個中間實體(如緩存服務器),而緩存服務最基本的要求是給用戶返回正確的文檔。如果服務端根據不同 User-Agent 返回不同內容,而緩存服務器把 IE6 用戶的響應緩存下來,并返回給使用其他瀏覽器的用戶,肯定會出問題 。因此,HTTP 協議規定,如果服務端提供的內容取決于 User-Agent 這樣「常規 Accept 協商字段之外」的請求頭字段,那么響應頭中必須包含 Vary 字段,且 Vary 的內容必須包含 User-Agent。同理,如果服務端同時使用請求頭中 User-Agent 和 Cookie 這兩個字段來生成內容,那么響應中的 Vary 字段看上去應該是這樣的:
Vary: User-Agent, Cookie
也就是說 Vary 字段用于列出一個響應字段列表,告訴緩存服務器遇到同一個 URL 對應著不同版本文檔的情況時,如何緩存和篩選合適的版本。
場景
1. 瀏覽器發起HTTP請求。
2. 請求達到緩存服務器。
3. 緩存服務器服務器查看自身是否存在請求的資源,如果存在,則將資源直接返回到瀏覽器,否則將請求轉發給web服務器。
4. web服務器處理請求,將響應的資源返回給緩存服務器。緩存服務器查看HTTP頭中是否包含Vary字段。如果有,則讀取對應的值,以及這些值對應的字段,按字段值分類存儲收到的響應資源。
5. 緩存服務器將響應資源返回給瀏覽器。
比如,Http Response 協議頭中含有Vary: Accept-Encoding,意味著該緩存服務器會根據協議頭中的Content-Encoding字段來分別緩存壓縮和非壓縮資源。當客戶端發起http請求時,緩存服務器會根據http協議頭中是否包含Accept-Encoding字段來決定是否返回壓縮過的資源。
|