Etag 和 If-None-Match


      ETag是HTTP1.1中才加入的一個屬性,用來幫助服務器控制Web端的緩存驗證
 
     它的原理是這樣的,當瀏覽器請求服務器的某項資源(A)時, 服務器根據A算出一個哈希值(3f80f-1b6-3e1cb03b)並通過 ETag 返回給瀏覽器,瀏覽器把"3f80f-1b6-3e1cb03b" 和 A 同時緩存在本地,當下次再次向服務器請求A時,會通過類似 If-None-Match: "3f80f-1b6-3e1cb03b" 的請求頭把ETag發送給服務器,服務器再次計算A的哈希值並和瀏覽器返回的值做比較,如果發現A發生了變化就把A返回給瀏覽器(200),如果發現A沒有變化就給瀏覽器返回一個304未修改。這樣通過控制瀏覽器端的緩存,可以節省服務器的帶寬,因為服務器不需要每次都把全量數據返回給客戶端。

     性能

      聰明的服務器開發者會把ETags和GET請求的“If-None-Match”頭一起使用,這樣可利用客戶端(例如瀏覽器)的緩存。因為服務器首先產生ETag,服務器可在稍后使用它來判斷頁面是否已經被修改。本質上,客戶端通過將該記號傳回服務器要求服務器驗證其(客戶端)緩存。
     其過程如下:
     1. 客戶端請求一個頁面(A)。 服務器返回頁面A,並在給A加上一個ETag。 客戶端展現該頁面,並將頁面連同ETag一起緩存。         2. 客戶再次請求頁面A,並將上次請求時服務器返回的ETag一起傳遞給服務器。
     3. 服務器檢查該ETag,並判斷出該頁面自上次客戶端請求之后還未被修改,直接返回響應304(未修改——Not Modified)和一
        個空的響應體。

     通常情況下,ETag更類似於資源指紋(fingerprints),如果資源發生變化了就會生成一個新的指紋,這樣可以快速的比較資源的變化。在服務器端實現中,很多情況下並不會用哈希來計算ETag,這會嚴重浪費服務器端資源,很多網站默認是禁用ETag的。有些情況下,可以把ETag退化,比如通過資源的版本或者修改時間來生成ETag。

     如果通過資源修改時間來生成ETag,那么效果和HTTP協議里面的另外一個控制屬性(Last-Modified)就雷同了,使用 Last-Modified 的問題在於它的精度在秒(s)的級別,比較適合不太敏感的靜態資源。

   請求流程

    Etag由服務器端生成,客戶端通過If-Match或者說If-None-Match這個條件判斷請求來驗證資源是否修改。常見的是使用If-None-Match.請求一個文件的流程可能如下
    第一次請求:
    1.客戶端發起 HTTP GET 請求一個文件;
    2.服務器處理請求,返回文件內容和一堆Header,當然包括Etag(例如"2e681a-6-5d044840")(假設服務器支持Etag生成和已經開啟了Etag).狀態碼200
   第二次請求:
   1.客戶端發起 HTTP GET 請求一個文件,注意這個時候客戶端同時發送一個If-None-Match頭,這個頭的內容就是第一次請求時服務器返回的Etag:2e681a-6-5d044840
   2.服務器判斷發送過來的Etag和計算出來的Etag匹配,因此If-None-Match為False,不返回200,返回304,客戶端繼續使用本地緩存
   流程很簡單,問題是,如果服務器又設置了Cache-Control:max-age和Expires呢,怎么辦?
   答案是同時使用,也就是說在完全匹配If-Modified-Since和If-None-Match即檢查完修改時間和Etag之后,服務器才能返回304.(不要陷入到底使用誰的問題怪圈)

   作用

    Etag 主要為了解決 Last-Modified 無法解決的一些問題。
      1、一些文件也許會周期性的更改,但是他的內容並不改變(僅僅改變的修改時間),這個時候我們並不希望客戶端認為這個文件被修改了,而重新GET;
      2、某些文件修改非常頻繁,比如在秒以下的時間內進行修改,(比方說1s內修改了N次),If-Modified-Since能檢查到的粒度是s級的,這種修改無法判斷(或者說UNIX記錄MTIME只能精確到秒)
     3、某些服務器不能精確的得到文件的最后修改時間;
    為此,HTTP/1.1引入了 Etag(Entity Tags).Etag僅僅是一個和文件相關的標記,可以是一個版本標記,比如說v1.0.0或者說"2e681a-6-5d044840"這么一串看起來很神秘的編碼。但是HTTP/1.1標准並沒有規定Etag的內容是什么或者說要怎么實現,唯一規定的是Etag需要放在""內。
 

  If-None-Match 

     這是一個條件式請求首部。對於GET 和 HEAD 請求方法來說,當且僅當服務器上沒有任何資源的 ETag 屬性值與這個首部中列出的相匹配的時候,服務器端會才返回所請求的資源,響應碼為  200  。對於其他方法來說,當且僅當最終確認沒有已存在的資源的  ETag 屬性值與這個首部中所列出的相匹配的時候,才會對請求進行相應的處理。

     對於  GET 和 HEAD 方法來說,當驗證失敗的時候,服務器端必須返回響應碼 304 (Not Modified,未改變)。對於能夠引發服務器狀態改變的方法,則返回 412 (Precondition Failed,前置條件失敗)。需要注意的是,服務器端在生成狀態碼為 304 的響應的時候,必須同時生成以下會存在於對應的 200 響應中的首部:Cache-Control、Content-Location、Date、ETag、Expires 和 Vary 。

    ETag 屬性之間的比較采用的是弱比較算法,即兩個文件除了每個比特都相同外,內容一致也可以認為是相同的。例如,如果兩個頁面僅僅在頁腳的生成時間有所不同,就可以認為二者是相同的。

       當與  If-Modified-Since  一同使用的時候,If-None-Match 優先級更高(假如服務器支持的話)。

      以下是兩個常見的應用場景:

  • 采用 GET 或 HEAD  方法,來更新擁有特定的ETag 屬性值的緩存。
  • 采用其他方法,尤其是  PUT,將 If-None-Match used 的值設置為 * ,用來生成事先並不知道是否存在的文件,可以確保先前並沒有進行過類似的上傳操作,防止之前操作數據的丟失。這個問題屬於更新丟失問題的一種。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM