一個URL經常須要表明若干不一樣的資源。例如那種須要以多種語言提供其內容的網站站點。若是某個站點有說法語的和說英語的兩種用戶,它可能想用這兩種語言提供網站站點信息。理想狀況下,服務器應當向英語用戶發送英文版,向法語用戶發送法文版——用戶只要訪問網站主頁就能夠獲得相應語言的內容html
HTTP提供了內容協商方法,容許客戶端和服務器做這樣的決定。經過這些方法,單一的URL就能夠表明不一樣的資源(好比,同一個網站頁面的法語版和英語版),這些不一樣的版本稱爲變體。本文將詳細介紹內容協商編程
對於特定的URL來講,服務器能夠根據一些原則來決定發送什麼內容給客戶端最合適。在有些場合下,服務器甚至能夠自動生成定製的頁面。好比,服務器能夠爲手持設備把HTML頁面轉換成WML頁面。這類動態內容變換被稱爲轉碼。這些變換動做是HTTP客戶端和服務器之間進行內容協商的結果瀏覽器
共有3種不一樣的方法能夠決定服務器上哪一個頁面最適合客戶端:讓客戶端來選擇、服務器自動斷定,或讓中間代理來選。這3種技術分別稱爲客戶端驅動的協商、服務器驅動的協商以及透明協商緩存
對於服務器來講,收到客戶端請求時只是發回響應,在其中列出可用的頁面,讓客戶端決定要看哪一個,這是最容易的事情。很顯然,這是服務器最容易實現的方式,並且客戶端極可能選擇到最佳的版本(只要列表中有讓客戶端選擇的足夠信息)。不利之處是每一個頁面都須要兩次請求:第一次獲取列表,第二次獲取選擇的副本。這種技術速度很慢且過程枯燥乏味,讓用戶厭煩安全
從實現原理上來講,服務器實際上有兩種方法爲客戶端提供選項:一是發送回一個HTML文檔,裏面有到該頁面的各類版本的連接和每一個版本的描述信息,另外一種方法是發送回HTTP/1.1響應時,使用300 Multiple Choices響應代碼。客戶端瀏覽器收到這種響應時,在前一種狀況下,會顯示一個帶有連接的頁面,在後一種狀況下,可能會彈出對話窗口,讓用戶作選擇。無論怎麼樣,決定是由客戶端的瀏覽器用戶做出的服務器
除了增長時延而且對每一個頁面都要進行繁瑣的屢次請求以外,這種方法還有一個缺點:它須要多個URL:公共頁面要一個,其餘每種特殊頁面也都要一個。所以,好比說原始的請求地址是www.joes-hardware.com,Joe的服務器可能會回覆某個頁面,該頁面裏面有到www.joes-hardware.com/english和www.joes-hardware.com/french的連接。若是客戶端想加書籤的話,是要加在原始的公共頁面上呢,仍是加在選中的頁面上呢?若是用戶想把這個網站推薦給他的朋友,是告知www.joes-hardware.com這個地址好呢,仍是隻告訴他們講英語的朋友www.joes-hardware.com/english這個地址?框架
減小額外通訊量的一種方法是讓服務器來決定發送哪一個頁面回去,但爲了作到這一點,客戶端必須發送有關客戶偏好的足夠信息,以便服務器可以做出準確的決策。服務器經過客戶端請求的首部集來得到這方面的信息網站
有如下兩種機制可供HTTP服務器評估發送什麼響應給客戶端比較合適編碼
一、檢査內容協商首部集。服務器察看客戶端發送的Accept首部集,設法用相應的響應首部與之匹配spa
二、根據其餘(非內容協商)首部進行變通。例如,服務器能夠根據客戶端發送的User-Agent首部來發送響應
【內容協商首部集】
客戶端能夠用下面列出的HTTP首部集發送用戶的偏好信息
首部 描述 Accept 告知服務器發送何種媒體類型 Accept-Language 告知服務器發送何種語言 Accept-Charset 告知服務器發送何種字符集 Accept-Encoding 告知服務器採用何種編碼
[注意]這些首部與實體首部很是相似。不過,這兩種首部的用途大相徑庭。實體首部集像運輸標籤,它們描述了把報文從服務器傳輸給客戶端的過程當中必須的各類報文主體屬性。而內容協商首部集是由客戶端發送給服務器用於交換偏好信息的,以便服務器能夠從文檔的不一樣版本中選擇出最符合客戶端偏好的那個來提供服務
服務器用下面列出的實體首部集來匹配客戶端的Accept首部集
Accept首部 實體首部 Accept Content-Type Accept-Language Content-Language Accept-Charset Content-Type Accept-Encoding Content-Encoding
因爲HTTP是無狀態的協議,表示服務器不會在不一樣的請求之間追蹤客戶端的偏好,因此客戶端必須在每一個請求中都發送其偏好信息
若是兩個客戶端都發送了Accept-Language首部,描述它們感興趣的語言信息,服務器就可以決定發送www.joes-hardware.com的何種版本給哪一個客戶端了。讓服務器自動選擇發送回去的文檔,減小了往返通訊的時延,這種時延是客戶端驅動模型中沒法避免的
然而,假設某個客戶端偏好西班牙文,那服務器應當回送哪一個版本的頁面呢?英語仍是法語?服務器只有兩種選擇:猜想或回退到客戶端驅動模型,問客戶端選擇哪一個。假如這個西班牙人碰巧懂一點英語,他可能會選擇英文頁面,這不是最理想的,但它能解決問題。在這種狀況下,這個西班牙人須要有辦法傳達更多與其偏好有關的信息,也就是他的確對英語略知一二,在沒有西班牙語的時候,英語也行
幸運的是,HTTP提供了一種機制,可讓與這個西班牙人狀況相似的客戶端更詳細地描述其偏好。這種機制就是質量值(簡稱q值)
HTTP協議中定義了質量值,容許客戶端爲每種偏好類別列出多種選項,併爲每種偏好選項關聯一個優先次序。例如,客戶端能夠發送下列形式的Accept-Language首部:
Accept-Language: en; q=0.5, fr; q=0.0 , nl; q=1.0, tr; q=0.0
其中q值的範圍從0.0-1.0(0.0是優先級最低的,而1.0是優先級最高的)。上面列出的那個首部,說明該客戶端最願意接收荷蘭語(縮寫爲nl)文檔,但英語(縮寫爲en)文檔也行;不管如何,這個客戶端都不肯意收到法語(縮寫爲fr)或土耳 其語(縮寫爲tr)的版本
[注意]偏好的排列順序並不重要,只有與偏好相關的q值纔是重要的
服務器偶爾也會碰到找不到文檔能夠匹配客戶端的任何偏好的狀況。對於這種狀況,服務器能夠修改文檔,也就是對文檔進行轉碼,以匹配客戶端的偏好
【其餘首部集】
服務器也能夠根據其餘客戶端請求首部集來匹配響應,好比User-Agent首部。例如,服務器知道老版本的瀏覽器不支持JavaScript語言,這樣就能夠向其發送不含有JavaScript的頁面版本
在這種狀況下,沒有q值機制可供査找「最近似」的匹配。服務器或者去找徹底匹配,或者簡單地有什麼就給什麼,這取決於服務器的實現
因爲緩存須要盡力提供所緩存文檔中正確的「最佳」版本,HTTP協議定義了服務器在響應中發送的Vary首部。這個首部告知緩存,還有客戶端和全部下游的代理,服務器根據哪些首部來決定發送響應的最佳版本
【Apache】
下面歸納了著名的Web服務器Apache是如何支持內容協商的。網站的內容提供者,好比說Joe要負責爲Joe的索引頁面提供不一樣的版本。Joe還必須把這些索引頁面文件放在和站點相關的Apache服務器的適當目錄下。用如下兩種方式能夠啓用內容協商
一、在網站目錄中,爲網站中每一個有變體的URI建立一個type-map(類型映射)文件。這個type-map文件列出了每一個變體和其相關的內容協商首部集
二、啓用MultiViews指令,這樣會使Apache自動爲目錄建立type-map文件
【使用type-map文件】
Apache服務器須要知道type-map文件的命名規則。能夠在服務器的配置文件中設置handler來講明type-map文件的後綴名。例如:
AddHandler type-map .var
這行就說明了後綴是.var的文件就是type-map文件
下面給出一個type-map文件示例
根據這個type-map文件,Apache服務器就知道要發送joes-hardware.en.html給請求英語版的客戶端,發送joes-hardware.fr.de.html給請求法語版的客戶端。Apache服務器也支持質量值
【使用MultiView】
爲了使用MultiView,必須在網站目錄下的access.conf文件中的適當小節(<Directory>、<Location>,或<Files>)使用OPTION指令來啓用它
若是啓用了MultiView,而瀏覽器又請求了名爲joes-hardware的資源,服務器就會査找全部名字中含有joes-hardware的文件,併爲它們建立type-map文件。服務器會根據名字猜想其對應的內容協商首部集。例如,法語版的joes-hardware應當含有.fr
另外一種在服務器端實現內容協商的方法是使用服務器端擴展,好比微軟的動態服務器頁面(Microsoft’s Active Server Pages, ASP)
透明協商機制試圖從服務器上去除服務器驅動協商所需的負載,並用中間代理來表明客戶端以使與客戶端的報文交換最小化。假定代理了解客戶端的預期,這樣就能夠表明客戶端與服務器協商,在客戶端請求內容的時候,代理已經收到了客戶端的預期
爲了支持透明內容協商,服務器必須有能力告知代理,服務器須要檢査哪些請求首部,以便對客戶端的請求進行最佳匹配。HTTP/1.1規範中沒有定義任何透明協商機制,但定義了Vary首部。服務器在響應中發送了Vary首部,以告知中間節點須要使用哪些請求首部進行內容協商
代理緩存能夠爲經過單個URL訪問的文檔保存不一樣的副本。若是服務器把它們的決策過程傳給緩存,這些代理就能表明服務器與客戶端進行協商。緩存同時也是進行內容轉碼的好地方,由於部署在緩存裏的通用轉碼器能對任意服務器,而不只僅是一臺服務器傳來的內容進行轉碼
【緩存與備用候選】
對內容進行緩存的時候是假設內容之後還能夠重用。然而,爲了確保對客戶端請求回送的是正確的已緩存響應,緩存必須應用服務器在回送響應時所用到的大部分決策邏輯
上面描述了客戶端發送的Accept首部集,以及爲了給每條請求選擇最佳的響應,服務器使用的與這些首部集匹配的相應實體首部集。緩存也必須使用相同的首部集來決定回送哪一個已緩存的響應
下圖展現了涉及緩存的正確及錯誤的操做序列。緩存把第一個請求轉發給服務器,並存儲其響應。對於第二個請求,緩存根據URL査找到了匹配的文檔。可是,這份文檔是法語版的,而請求者想要的是西班牙語版的。若是緩存只是把文檔的法語版本發給請求者的話,它就犯了錯誤
所以,緩存也應該把第二條請求轉發給服務器,並保存該URL的響應與「備用候選」響應。緩存如今就保存了同一個URL的兩份不一樣的文檔,與服務器上同樣。這些不一樣的版本稱爲變體(variant)或備用候選(alternate)。內容協商可當作是爲客戶端請求選擇最合適變體的過程
【Vary 首部】
這裏是瀏覽器和服務器發送的一些典型的請求及響應首部
然而,若是服務器的決策不是依據Accept首部集,而是好比User-Agent首部的話,狀況會如何?這不像聽起來這麼極端。例如,服務器可能知道老版本的瀏覽器不支持JavaScript語言,所以可能會回送不包含JavaScript的頁面版本。若是服務器是根據其餘首部來決定發送哪一個頁面的話,緩存必須知道這些首部是什麼,這樣才能在選擇回送的頁面時作出一樣的邏輯判斷
HTTP的Vary響應首部中列出了全部客戶端請求首部,服務器可用這些首部來選擇文檔或產生定製的內容(在常規的內容協商首部集以外的內容)。例如,若所提供的文檔取決於User-Agent首部,Vary首部就必須包含User-Agent
當新的請求到達時,緩存會根據內容協商首部集來尋找最佳匹配。但在把文檔提供給客戶端以前,它必須檢査服務器有沒有在已緩存響應中發送Vary首部。若是有Vary首部,那麼新請求中那些首部的值必須與舊的已緩存請求裏相應的首部相同。由於服務器可能會根據客戶端請求的首部來改變響應,爲了實現透明協商,緩存必須爲每一個已緩存變體保存客戶端請求首部和相應的服務器響應首部,參見下圖
若是某服務器的Vary首部看起來像下面這樣,大量不一樣的User-Agent和Cookie值將會產生很是多的變體:
Vary: User-Agent, Cookie
緩存必須爲每一個變體保存其相應的文檔版本。當緩存執行査找時,首先會對內容協商首部集進行內容匹配,而後比較請求的變體與緩存的變體。若是沒法匹配,緩存就從原始服務器獲取文檔
咱們已經討論了一個機制,該機制可讓客戶端和服務器從某個URL的一系列文檔中挑選出最適合客戶端的文檔。實現這些機制的前提是,存在一些知足客戶端需求的文檔——無論是徹底知足仍是在必定程度上知足
然而,若是服務器沒有能知足客戶端需求的文檔會怎麼樣呢?服務器能夠給出一個錯誤響應。但理論上,服務器能夠把現存的文檔轉換成某種客戶端可用的文檔。這種選項稱爲轉碼
下面列出了一些假設的轉碼
轉換以前 轉換以後
HTML文檔 WML文檔
高分辨率圖像 低分辨率圖像
彩色圖像 黑白圖像
有多個框架的複雜頁面 沒有不少框架或圖像的簡單文本頁面
有Java小應用程序的HTML頁面 沒有Java小應用程序的HTML頁面
有廣告的頁面 去除廣告的頁面
有3種類別的轉碼:格式轉換、信息綜合以及內容注入
【格式轉換】
格式轉換是指將數據從一種格式轉換成另外一種格式,使之能夠被客戶端査看。經過HTML到WML的轉換,無線設備就能夠訪問一般供桌面客戶端査看的文檔了。經過慢速鏈接訪問Web頁面的客戶端並不須要接收高分辨率圖像,若是經過格式轉換下降圖像分辨率和顏色來減少圖像文件大小的話,這類客戶端就能更容易地査看圖像比較豐富的頁面了
格式轉換能夠由內容協商首部集來驅動,但也能由User-Agent首部來驅動。注意,內容轉換或轉碼與內容編碼或傳輸編碼是不一樣的,後二者通常用於更高效或安全地傳輸內容,而前二者則可以使訪問設備可以査看內容
【信息綜合】
從文檔中提取關鍵的信息片斷稱爲信息綜合(information synthesis),這是一種有用的轉碼操做。這種操做的例子包括根據小節標題生成文檔的大綱,或者從頁面中刪除廣告和商標
根據內容中的關鍵字對頁面分類是更精細的技術,有助於總結文檔的精髓。這種技術經常使用於Web頁面分類系統中,好比門戶網站的Web頁面目錄
【內容注入】
前面描述的兩類轉碼一般會減小Web文檔的內容,但還有另外一類轉換會增長文檔的內容,即內容注入轉碼。內容注入轉碼的例子有自動廣告生成器和用戶追蹤系統
設想一下,一個能往途經的每一個HTML頁面中自動添加廣告的廣告植入轉碼器是多麼的誘人,固然也很煩人。這類轉碼操做只能動態進行——它必須即時添加與當前的特定用戶有關,或針對特定用戶的廣告。也能夠構建用戶追蹤系統,在頁面中動態增長內容,用於收集用戶査看頁面和客戶端瀏覽方式的統計信息
【轉碼與靜態預生成的對比】
轉碼的替代作法是在Web服務器上創建Web頁面的不一樣副本,例如一個是HTML,一個是WML;一個圖像分辨率高,一個圖像分辨率低;一個有多媒體內容,一個沒有。可是,這種方法不是很切合實際,緣由不少:某個頁面中的任何小改動都會牽扯不少頁面,須要不少空間來存儲各頁面的不一樣版本,並且使頁面編目和Web服務器編程(以提供正確的版本)變得更加困難。有些轉碼操做,好比廣告插入(尤爲是定向廣告插入),就不能靜態實現——由於插入什麼廣告和請求頁面的用戶有關
對單一的根頁面進行即時轉換,是比靜態的預生成更容易的解決方案。但這樣會在提供內容時增長時延。不過有時候其中一些計算能夠由第三方進行,這樣就減小了Web服務器上的計算負荷——好比能夠由代理或緩存中的外部Agent完成轉換
下圖顯示了在代理緩存中進行的轉碼