?
本文檔使用 PHP中文網(wǎng)手冊(cè) 發(fā)布
HTTP具有條件請(qǐng)求 的概念,通過(guò)比較受影響的資源和驗(yàn)證器的值,可以更改結(jié)果,甚至請(qǐng)求的成功。這些請(qǐng)求可用于驗(yàn)證緩存的內(nèi)容,避免無(wú)用的控制,驗(yàn)證文檔的完整性(例如恢復(fù)下載時(shí)),或防止在上載或修改服務(wù)器上的文檔時(shí)丟失更新。
HTTP條件請(qǐng)求是執(zhí)行不同的請(qǐng)求,具體取決于特定標(biāo)頭的值。這些頭文件定義了一個(gè)先決條件,如果前提條件匹配,請(qǐng)求的結(jié)果將會(huì)不同。
不同的行為由所使用的請(qǐng)求的方法以及用于前提條件的一組標(biāo)題定義:
對(duì)于通常試圖獲取文檔的安全方法,GET
條件請(qǐng)求可用于發(fā)回文檔(如果相關(guān)的話)。因此,這節(jié)省了帶寬。
對(duì)于不安全的方法,例如PUT
通常上傳文檔的情況,條件請(qǐng)求可以用于上載文檔,只有當(dāng)它基于的原始文檔與存儲(chǔ)在服務(wù)器上的文檔相同時(shí)。
所有條件標(biāo)題都會(huì)嘗試檢查服務(wù)器上存儲(chǔ)的資源是否與特定版本匹配。為了實(shí)現(xiàn)這一點(diǎn),條件請(qǐng)求需要指示資源的版本。由于將整個(gè)資源字節(jié)與字節(jié)進(jìn)行比較是不切實(shí)際的,并不總是需要的,所以請(qǐng)求傳輸描述版本的值。這些值稱為驗(yàn)證器,有兩種:
文檔上次修改日期,即上次修改日期。
一個(gè)不透明的字符串,唯一標(biāo)識(shí)每個(gè)版本,稱為實(shí)體標(biāo)簽或etag。
比較相同資源的版本有點(diǎn)棘手:根據(jù)上下文,有兩種相等性檢查:
當(dāng)期望字節(jié)到字節(jié)的身份時(shí),例如在恢復(fù)下載時(shí)使用強(qiáng)驗(yàn)證。
當(dāng)用戶代理只需要確定兩個(gè)資源是否具有相同的內(nèi)容時(shí)使用弱驗(yàn)證。即使他們是微小的差異,就像不同的廣告,或不同的日期頁(yè)腳。
驗(yàn)證的類型與所使用的驗(yàn)證器無(wú)關(guān)。雙方Last-Modified
并ETag
允許這兩種類型的驗(yàn)證,雖然復(fù)雜性來(lái)實(shí)現(xiàn)它在服務(wù)器端可能會(huì)有所不同。HTTP默認(rèn)使用強(qiáng)大的驗(yàn)證,并指定何時(shí)可以使用弱驗(yàn)證。
強(qiáng)有力的驗(yàn)證包括保證資源是字節(jié)到字節(jié),與它所比較的資源相同。這對(duì)于一些條件標(biāo)頭是必需的,對(duì)其他標(biāo)簽是默認(rèn)的。強(qiáng)大的驗(yàn)證非常嚴(yán)格,可能難以在服務(wù)器級(jí)別保證,但它確保在任何時(shí)候都不會(huì)丟失數(shù)據(jù),有時(shí)會(huì)犧牲性能。
使用一個(gè)獨(dú)特的標(biāo)識(shí)符來(lái)進(jìn)行強(qiáng)有力的驗(yàn)證是非常困難的Last-Modified
。通常這是通過(guò)使用ETag
資源的MD5散列(或派生)來(lái)完成的。
弱驗(yàn)證與強(qiáng)驗(yàn)證不同,因?yàn)樗J(rèn)為如果內(nèi)容是相同的,則文檔的兩個(gè)版本是相同的。例如,只有頁(yè)腳中的日期不同或其他廣告與另一頁(yè)不同的頁(yè)面會(huì)被視為與驗(yàn)證較弱的頁(yè)面完全相同。這些相同的兩個(gè)版本在使用強(qiáng)大驗(yàn)證時(shí)被認(rèn)為是不同的 構(gòu)建一個(gè)etags系統(tǒng)會(huì)產(chǎn)生弱驗(yàn)證,這可能很復(fù)雜,因?yàn)樗婕傲私忭?yè)面不同元素的重要性,但對(duì)優(yōu)化高速緩存性能非常有用。
一些稱為條件標(biāo)題的HTTP標(biāo)題會(huì)導(dǎo)致有條件的請(qǐng)求。這些是:
如果ETag
遙遠(yuǎn)的資源等于此標(biāo)題中列出的資源,If-Match
則成功。默認(rèn)情況下,除非etag帶有前綴'W/'
,否則會(huì)執(zhí)行強(qiáng)有力的驗(yàn)證。If-None-Match
如果ETag
遠(yuǎn)程資源的不同于此標(biāo)題中列出的每個(gè)資源,則成功。默認(rèn)情況下,除非etag帶有前綴'W/'
,否則會(huì)執(zhí)行強(qiáng)有力的驗(yàn)證。如果Last-Modified
距離資源的日期比此標(biāo)題中給出的日期更近,If-Modified-Since
則成功。如果Last-Modified
遙遠(yuǎn)資源的日期較舊或與此標(biāo)題中給出的日期相同,If-Unmodified-Since
則成功。If-Range
與If-Match
或類似If-Unmodified-Since
,但可以只有一個(gè)單一的etag或一個(gè)日期。如果失敗,范圍請(qǐng)求失敗,而不是206Partial Content
響應(yīng)200OK
與完整的資源一起發(fā)送。
條件請(qǐng)求最常見(jiàn)的用例是更新緩存。使用空緩存或無(wú)緩存時(shí),請(qǐng)求的資源將以狀態(tài)返回200
OK
。
與資源一起,驗(yàn)證器將在標(biāo)題中發(fā)送。在這個(gè)例子中,無(wú)論是Last-Modified
和ETag
發(fā)送,但它同樣一直只是其中之一。這些驗(yàn)證器會(huì)與資源一起緩存(如所有頭文件),并在緩存過(guò)期時(shí)用于創(chuàng)建條件請(qǐng)求。
只要緩存不陳舊,根本就不會(huì)發(fā)出請(qǐng)求。但是一旦它變得陳舊,這主要由Cache-Control
頭部控制,客戶端不直接使用緩存值,而是發(fā)出條件請(qǐng)求。驗(yàn)證程序的值用作If-Modified-Since
和If-Match
標(biāo)題的參數(shù)。
如果資源沒(méi)有改變,服務(wù)器發(fā)回一個(gè)304
Not Modified
響應(yīng)。這使得緩存再次變得新鮮,并且客戶端使用緩存的資源。盡管響應(yīng)/請(qǐng)求往返消耗了一些資源,但這比通過(guò)線路傳輸整個(gè)資源更有效。
如果資源發(fā)生了變化,那么服務(wù)器只是發(fā)送一個(gè)200
OK
響應(yīng),并帶有新版本的資源,就像請(qǐng)求不是有條件的并且客戶端使用這個(gè)新資源(并對(duì)其進(jìn)行緩存)一樣。
除了在服務(wù)器端設(shè)置驗(yàn)證器之外,這種機(jī)制是透明的:所有瀏覽器管理一個(gè)緩存并發(fā)送這樣的條件請(qǐng)求,而不需要Web開(kāi)發(fā)人員完成任何特殊工作。
文件的部分下載是HTTP的功能,允許恢復(fù)先前的操作,通過(guò)保留已獲得的信息來(lái)節(jié)省帶寬和時(shí)間:
支持部分下載的服務(wù)器通過(guò)發(fā)送Accept-Ranges
標(biāo)題來(lái)廣播此內(nèi)容。一旦發(fā)生這種情況,客戶端可以通過(guò)發(fā)送Ranges
缺少范圍的標(biāo)題來(lái)恢復(fù)下載:
原理很簡(jiǎn)單,但是存在一個(gè)潛在的問(wèn)題:如果下載的資源在兩次下載之間被修改,則獲得的范圍將對(duì)應(yīng)于資源的兩個(gè)不同版本,并且最終文檔將被損壞。
為了防止這種情況,使用條件請(qǐng)求。對(duì)于范圍,有兩種方法可以做到這一點(diǎn)。如果前提條件失敗,則使用更靈活的If-Modified-Since
和If-Match
和服務(wù)器返回錯(cuò)誤; 客戶端從頭開(kāi)始重新下載:
即使此方法起作用,它在文檔發(fā)生更改時(shí)也會(huì)添加額外的響應(yīng)/請(qǐng)求交換。這會(huì)影響性能,并且HTTP有一個(gè)特定的頭部以避免這種情況If-Range
::
該解決方案效率更高,但靈活性稍差,因?yàn)樵谠撉闆r下只能使用一個(gè)etag。很少需要這種額外的靈活性。
Web應(yīng)用程序中的常見(jiàn)操作是更新遠(yuǎn)程文檔。這在任何文件系統(tǒng)或源代碼管理應(yīng)用程序中都很常見(jiàn),但任何允許存儲(chǔ)遠(yuǎn)程資源的應(yīng)用程序都需要這種機(jī)制。常見(jiàn)的網(wǎng)站,比如維基和其他CMS,都有這樣的需求。
用這個(gè)PUT
方法你可以實(shí)現(xiàn)這個(gè)目的。客戶端首先讀取原始文件,修改它們,最后將它們推送到服務(wù)器:
不幸的是,只要我們考慮到并發(fā)性,事情就會(huì)有點(diǎn)不準(zhǔn)確。當(dāng)客戶端在本地修改資源的新副本時(shí),第二個(gè)客戶端可以獲取相同的資源并在其副本上執(zhí)行相同的操作。接下來(lái)發(fā)生的事情非常不幸:當(dāng)他們回到服務(wù)器時(shí),第一個(gè)客戶端的修改被下一個(gè)客戶端推送所拋棄,因?yàn)檫@第二個(gè)客戶端并不知道第一個(gè)客戶端對(duì)資源的更改。誰(shuí)贏的決定不會(huì)傳達(dá)給對(duì)方。哪些客戶的變化將被保留,會(huì)隨著他們提交的速度而變化; 這取決于客戶端,服務(wù)器的性能,甚至是在客戶端編輯文檔的人員。獲勝者將從一次改變到下一次。這是一個(gè)競(jìng)賽條件 并導(dǎo)致難以檢測(cè)和調(diào)試的有問(wèn)題的行為:
如果不打擾兩個(gè)客戶之一,就無(wú)法處理這個(gè)問(wèn)題。但是,要避免丟失的更新和競(jìng)賽條件。我們希望得到可預(yù)測(cè)的結(jié)果,并期望在客戶的更改被拒絕時(shí)通知客戶。
有條件的請(qǐng)求允許實(shí)現(xiàn)積極鎖定算法(由大多數(shù)維基或源代碼控制系統(tǒng)使用)。其概念是允許所有客戶端獲得資源副本,然后讓他們?cè)诒镜匦薷乃?,通過(guò)成功地允許第一個(gè)客戶端提交更新來(lái)控制并發(fā)性。所有后續(xù)更新(基于資源的過(guò)時(shí)版本)都會(huì)被拒絕:
這是使用If-Match
或If-Unmodified-Since
頭來(lái)實(shí)現(xiàn)的。如果etag與原始文件不匹配,或者文件自獲取后已被修改,則更改將被拒絕并出現(xiàn)412
Precondition Failed
錯(cuò)誤。然后由客戶來(lái)處理錯(cuò)誤:通過(guò)通知用戶重新開(kāi)始(這次是最新版本),或者通過(guò)向用戶顯示兩個(gè)版本的差異,幫助他們確定他們希望保留哪些改變。
資源的第一次上傳是以前的一個(gè)邊緣案例。與資源的任何更新一樣,如果兩個(gè)客戶端嘗試在相似時(shí)間執(zhí)行,則會(huì)受到競(jìng)爭(zhēng)狀況的影響。為了防止這種情況,可以使用條件請(qǐng)求:通過(guò)添加If-None-Match
特殊值'*'
,代表任何etag。只有在資源不存在之前,該請(qǐng)求才會(huì)成功:
If-None-Match
將僅適用于HTTP / 1.1(及更高版本)兼容的服務(wù)器。如果不確定服務(wù)器是否符合要求,則需要首先向HEAD
資源發(fā)出請(qǐng)求以檢查該問(wèn)題。
有條件的請(qǐng)求是HTTP的一個(gè)關(guān)鍵功能,并允許構(gòu)建高效和復(fù)雜的應(yīng)用程序。為了緩存或恢復(fù)下載,網(wǎng)站站長(zhǎng)只需要正確配置服務(wù)器; 在一些環(huán)境中設(shè)置正確的etags可能會(huì)很棘手。一旦實(shí)現(xiàn),瀏覽器將滿足預(yù)期的條件請(qǐng)求。
對(duì)于鎖定機(jī)制,情況恰恰相反:Web開(kāi)發(fā)人員需要使用正確的標(biāo)題發(fā)出請(qǐng)求,而網(wǎng)站管理員可以主要依靠應(yīng)用程序來(lái)執(zhí)行對(duì)它們的檢查。
在這兩種情況下,很顯然,有條件的請(qǐng)求是Web背后的基本特征。