HTTP 304狀態碼的詳細講解

HTTP 304狀態碼的詳細講解


304狀態碼或許不應該認爲是一種錯誤,而是對客戶端有緩存情況下服務端的一種響應。

整個請求響應過程如下:


客戶端在請求一個文件的時候,發現自己緩存的文件有 Last Modified ,那麼在請求中會包含 If Modified Since ,這個時間就是緩存文件的 Last Modified 。因此,如果請求中包含 If Modified Since,就說明已經有緩存在客戶端。服務端只要判斷這個時間和當前請求的文件的修改時間就可以確定是返回 304 還是 200 。
對於靜態文件,例如:CSS、圖片,服務器會自動完成 Last Modified 和 If Modified Since 的比較,完成緩存或者更新。但是對於動態頁面,就是動態產生的頁面,往往沒有包含 Last Modified 信息,這樣瀏覽器、網關等都不會做緩存,也就是在每次請求的時候都完成一個 200 的請求。
因此,對於動態頁面做緩存加速,首先要在 Response 的 HTTP Header 中增加 Last Modified 定義,其次根據 Request 中的 If Modified Since 和被請求內容的更新時間來返回 200 或者 304 。雖然在返回 304 的時候已經做了一次數據庫查詢,但是可以避免接下來更多的數據庫查詢,並且沒有返回頁面內容而只是一個 HTTP Header,從而大大的降低帶寬的消耗,對於用戶的感覺也是提高。當這些緩存有效的時候,通過 Fiddler 或HttpWatch 查看一個請求會得到這樣的結果:


第一次訪問 200
按F5刷新(第二次訪問) 304
按Ctrl+F5強制刷新 200

下面用Fiddler來查看上面的訪問請求過程

第一次(首次)訪問 200



第二次F5刷新訪問 304


請求的頭信息裏多了 “If-Modified-Since","If-None-Match" 


第三次 按Ctrl+F5強制刷新 200

同第一次,不貼圖了

爲什麼要使用條件請求

當用戶訪問一個網頁時,條件請求可以加速網頁的打開時間(因爲可以省去傳輸整個響應體的時間),但仍然會有網絡延遲,因爲瀏覽器還是得爲每個資源生成一條條件請求,並且等到服務器返回HTTP/304響應,才能讀取緩存來顯示網頁.更理想的情況是,服務器在響應上指定Cache-Control或Expires指令,這樣客戶端就能知道該資源的可用時間爲多長,也就能跳過條件請求的步驟,直接使用緩存中的資源了.可是,即使服務器提供了這些信息,在下列情況下仍然需要使用條件請求:

在超過服務器指定的過期時間之後
如果用戶執行了刷新操作的話
在上節給出的圖片中,請求頭中包含了一個Pragma: no-cache.這是由於用戶使用F5刷新了網頁.如果用戶按下了CTRL-F5 (有時稱之爲“強刷-hard refresh”),你會發現瀏覽器省略了If-Modified-Since和If-None-Match請求頭,也就是無條件的請求頁面中的每個資源.

避免條件請求
通常來說,緩存是個好東西.如果你想提高自己網站的訪問速度,緩存是必須要考慮的.可是在調試的時候,有時候需要阻止緩存,這樣才能確保你所訪問到的資源是最新的.

你也許會有個疑問:“如果不改變網站內容,我怎麼才能讓Fiddler不返回304而返回一個包含響應體的HTTP/200響應呢?”

你可以在Fiddler中的網絡會話(Web Sessions)列表中選擇一條響應爲HTTP/304的會話,然後按下U鍵.Fiddler將會無條件重發(Unconditionally reissue)這個請求.然後使用命compare命令對比一下兩個請求有什麼不同,對比結果如下,從中可以得知,Fiddler是通過省略條件請求頭來實現無緩存請求的:

Screenshot of Windiff of conditional and unconditional requests

如果你想全局阻止HTTP/304響應,可以這麼做:首先清除瀏覽器的緩存,可以使用Fiddler工具欄上的Clear Cache按鈕(僅能清除Internet Explorer緩存),或者在瀏覽器上按CTRL+SHIFT+DELETE(所有瀏覽器都支持).在清除瀏覽器的緩存之後,回到Fiddler中,在菜單中選擇Rules > Performance > Disable Caching選項,然後Fiddler就會:刪除所有請求中的條件請求相同的請求頭以及所有響應中的緩存時間相關的響應頭.此外,還會在每個請求中添加Pragma: no-cache請求頭,在每個響應中添加Cache-Control: no-cache響應頭,阻止瀏覽器緩存這些資源.

動態網頁如何設置304

以aspx頁面爲例,代碼如下:

[csharp] view plain copy
  1. var request = context.Request;  
  2.             var response = context.Response;  
  3.             if (request.Headers["If-Modified-Since"].NotNullOrEmpty() || request.Headers["If-None-Match"].NotNullOrEmpty())  
  4.             {  
  5.                 response.StatusCode = 304;  
  6.                 return;  
  7.             }  
  8. //非304情況下的操作 略  
  9. //設置緩存選項  
  10.             response.Clear();  
  11.             response.ClearContent();  
  12.             response.Headers["Last-Modified"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");  
  13.             response.Headers["ETag"] = id;//這裏假設的是根據不同的id  
  14.             response.CacheControl = "private";  
  15.             response.ExpiresAbsolute = DateTime.Now.AddMonths(6);  
  16.              

ETag是什麼意思?

HTTP 協議規格說明定義ETag爲“被請求變量的實體值” 。 另一種說法是,ETag是一個可以與Web資源關聯的記號(token)。典型的Web資源可以一個Web頁,但也可能是JSON或XML文檔。服務器單獨負責判斷記號是什麼及其含義,並在HTTP響應頭中將其傳送到客戶端

asp.net web api的實現代碼如下:

[csharp] view plain copy
  1. // GET /images/001.png   
  2. [HttpGet]   
  3. public HttpResponseMessage Get(string filename)   
  4. {   
  5.     HttpResponseMessage response = new HttpResponseMessage();    
  6.   
  7.     .....  
  8.     string etag = string.Format("\"{0}\"", fileInfo.MD5);   
  9.     var tag = Request.Headers.IfNoneMatch.FirstOrDefault();   
  10.     if (Request.Headers.IfModifiedSince.HasValue && tag != null && tag.Tag == etag)   
  11.     {   
  12.         response.StatusCode = HttpStatusCode.NotModified;   
  13.     }   
  14.     else   
  15.     {   
  16.         //dealcode ......  
  17.         responseStream.Position = 0;   
  18.         response.StatusCode = fullContent ? HttpStatusCode.OK : HttpStatusCode.PartialContent;   
  19.         response.Content = new StreamContent(responseStream);   
  20.         response.Content.Headers.ContentType = new MediaTypeHeaderValue(fileInfo.ContentType);   
  21.         response.Headers.ETag = new EntityTagHeaderValue(etag);   
  22.         response.Headers.CacheControl = new CacheControlHeaderValue();   
  23.         response.Headers.CacheControl.Public = true;   
  24.         response.Headers.CacheControl.MaxAge = TimeSpan.FromHours(480);   
  25.         response.Content.Headers.Expires = DateTimeOffset.Now.AddDays(20);   
  26.         response.Content.Headers.LastModified = fileInfo.UploadDate;   
  27.     }   
  28.     return response;  
  29. }  


常見狀態碼:

一些常見的狀態碼爲:

  • 200 – 服務器成功返回網頁
  • 404 – 請求的網頁不存在
  • 503 – 服務器超時

下面提供 HTTP 狀態碼的完整列表。點擊鏈接可瞭解詳情。您也可以訪問 HTTP 狀態碼上的 W3C 頁獲取更多信息

1xx(臨時響應)
表示臨時響應並需要請求者繼續執行操作的狀態碼。

100(繼續)請求者應當繼續提出請求。服務器返回此代碼表示已收到請求的第一部分,正在等待其餘部分。
101(切換協議)請求者已要求服務器切換協議,服務器已確認並準備切換。

2xx (成功)

表示成功處理了請求的狀態碼。

200(成功)服務器已成功處理了請求。通常,這表示服務器提供了請求的網頁。如果是對您的 robots.txt 文件顯示此狀態碼,則表示 Googlebot 已成功檢索到該文件。
201(已創建)請求成功並且服務器創建了新的資源。
202(已接受)服務器已接受請求,但尚未處理。
203(非授權信息)服務器已成功處理了請求,但返回的信息可能來自另一來源。
204(無內容)服務器成功處理了請求,但沒有返回任何內容。
205(重置內容)服務器成功處理了請求,但沒有返回任何內容。與 204 響應不同,此響應要求請求者重置文檔視圖(例如,清除表單內容以輸入新內容)。
206(部分內容)服務器成功處理了部分 GET 請求。

3xx (重定向)
要完成請求,需要進一步操作。通常,這些狀態碼用來重定向。Google 建議您在每次請求中使用重定向不要超過 5 次。您可以使用網站管理員工具查看一下 Googlebot 在抓取重定向網頁時是否遇到問題。診斷下的網絡抓取頁列出了由於重定向錯誤導致 Googlebot 無法抓取的網址。

300(多種選擇)針對請求,服務器可執行多種操作。服務器可根據請求者 (user agent) 選擇一項操作,或提供操作列表供請求者選擇。
301(永久移動)請求的網頁已永久移動到新位置。服務器返回此響應(對 GET 或 HEAD 請求的響應)時,會自動將請求者轉到新位置。您應使用此代碼告訴 Googlebot 某個網頁或網站已永久移動到新位置。
302(臨時移動)服務器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來響應以後的請求。此代碼與響應 GET 和 HEAD 請求的 301 代碼類似,會自動將請求者轉到不同的位置,但您不應使用此代碼來告訴 Googlebot 某個網頁或網站已經移動,因爲 Googlebot 會繼續抓取原有位置並編制索引。
303(查看其他位置)請求者應當對不同的位置使用單獨的 GET 請求來檢索響應時,服務器返回此代碼。對於除 HEAD 之外的所有請求,服務器會自動轉到其他位置。
304(未修改)

自從上次請求後,請求的網頁未修改過。服務器返回此響應時,不會返回網頁內容。

如果網頁自請求者上次請求後再也沒有更改過,您應將服務器配置爲返回此響應(稱爲 If-Modified-Since HTTP 標頭)。服務器可以告訴 Googlebot 自從上次抓取後網頁沒有變更,進而節省帶寬和開銷。


305(使用代理)請求者只能使用代理訪問請求的網頁。如果服務器返回此響應,還表示請求者應使用代理。
307(臨時重定向)服務器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來響應以後的請求。此代碼與響應 GET 和 HEAD 請求的 <a href=answer.py?answer=>301</a> 代碼類似,會自動將請求者轉到不同的位置,但您不應使用此代碼來告訴 Googlebot 某個頁面或網站已經移動,因爲 Googlebot 會繼續抓取原有位置並編制索引。

4xx(請求錯誤)
這些狀態碼錶示請求可能出錯,妨礙了服務器的處理。

400(錯誤請求)服務器不理解請求的語法。
401(未授權)請求要求身份驗證。對於登錄後請求的網頁,服務器可能返回此響應。
403(禁止)服務器拒絕請求。如果您在 Googlebot 嘗試抓取您網站上的有效網頁時看到此狀態碼(您可以在 Google 網站管理員工具診斷下的網絡抓取頁面上看到此信息),可能是您的服務器或主機拒絕了 Googlebot 訪問。
404(未找到)

服務器找不到請求的網頁。例如,對於服務器上不存在的網頁經常會返回此代碼。

如果您的網站上沒有 robots.txt 文件,而您在 Google 網站管理員工具“診斷”標籤的 robots.txt 頁上看到此狀態碼,則這是正確的狀態碼。但是,如果您有 robots.txt 文件而又看到此狀態碼,則說明您的 robots.txt 文件可能命名錯誤或位於錯誤的位置(該文件應當位於頂級域,名爲 robots.txt)。

如果對於 Googlebot 抓取的網址看到此狀態碼(在”診斷”標籤的 HTTP 錯誤頁面上),則表示 Googlebot 跟隨的可能是另一個頁面的無效鏈接(是舊鏈接或輸入有誤的鏈接)。

405(方法禁用)禁用請求中指定的方法。
406(不接受)無法使用請求的內容特性響應請求的網頁。
407(需要代理授權)此狀態碼與 <a href=answer.py?answer=35128>401(未授權)</a>類似,但指定請求者應當授權使用代理。如果服務器返回此響應,還表示請求者應當使用代理。
408(請求超時)服務器等候請求時發生超時。
409(衝突)服務器在完成請求時發生衝突。服務器必須在響應中包含有關衝突的信息。服務器在響應與前一個請求相沖突的 PUT 請求時可能會返回此代碼,以及兩個請求的差異列表。
410(已刪除)如果請求的資源已永久刪除,服務器就會返回此響應。該代碼與 404(未找到)代碼類似,但在資源以前存在而現在不存在的情況下,有時會用來替代 404 代碼。如果資源已永久移動,您應使用 301 指定資源的新位置。
411(需要有效長度)服務器不接受不含有效內容長度標頭字段的請求。
412(未滿足前提條件)服務器未滿足請求者在請求中設置的其中一個前提條件。
413(請求實體過大)服務器無法處理請求,因爲請求實體過大,超出服務器的處理能力。
414(請求的 URI 過長)請求的 URI(通常爲網址)過長,服務器無法處理。
415(不支持的媒體類型)請求的格式不受請求頁面的支持。
416(請求範圍不符合要求)如果頁面無法提供請求的範圍,則服務器會返回此狀態碼。
417(未滿足期望值)服務器未滿足”期望”請求標頭字段的要求。

5xx(服務器錯誤)
這些狀態碼錶示服務器在處理請求時發生內部錯誤。這些錯誤可能是服務器本身的錯誤,而不是請求出錯。

500(服務器內部錯誤)服務器遇到錯誤,無法完成請求。
501(尚未實施)服務器不具備完成請求的功能。例如,服務器無法識別請求方法時可能會返回此代碼。
502(錯誤網關)服務器作爲網關或代理,從上游服務器收到無效響應。
503(服務不可用)服務器目前無法使用(由於超載或停機維護)。通常,這只是暫時狀態。
504(網關超時)服務器作爲網關或代理,但是沒有及時從上游服務器收到請求。
505(HTTP 版本不受支持)服務器不支持請求中所用的 HTTP 協議版本。


引用內容源址:

http://www.jb51.net/article/43143.htm

http://blog.sina.com.cn/s/blog_4c98b9600100jd4z.html

http://www.cnblogs.com/shanyou/archive/2012/05/01/2477500.html

--- end ---

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章