在我們開發一個ASP.NET網站的過程中,其實有很多地方都是可以使用緩存的, 只是由于ASP.NET是一種基于服務端的開發平臺,自然我們也經常在服務端的代碼中使用各類緩存技術, 然而,由于WEB應用程序的服務對象是客戶端的瀏覽器,通常來說,我們并不能直接控制瀏覽器的行為,但是, 瀏覽器卻可以根據后臺網站的指示,采取一些優化的方式來更快地呈現頁面。 客戶端瀏覽器也有自己的緩存機制,通常瀏覽器也使用緩存來優化一些頁面的顯示過程, 不過,我們并不能直接使用C#代碼控制瀏覽器的緩存操作,但我們可以告訴瀏覽器如何使用緩存,從而達到優化網站性能的目的。
這次博客的主題是:用ASP.NET控制HTTP請求過程中瀏覽器緩存的一些方法。
正常的HTTP請求過程
在開始介紹瀏覽器在HTTP請求過程前,我想有必要先來看一下瀏覽器請求一個普通ASPX頁面的過程。
說明:本文在介紹HTTP請求過程時,會大量使用Fiddler來分析具體的請求過程。
上圖是一個普通的ASPX頁面的請求過程,說它普通是因為:我在創建這個頁面后,沒對它做任何緩存方面的處理。
圖片中我們可以可以看到服務器的響應狀態為:HTTP/1.1 200 OK,這是一個服務器成功響應的標志。
另外,要注意圖片中的Cache響應頭部分,我之所以就紅線框出來,是想提醒您注意這塊的內容將在后面的小節中發生改變, 到時候請注意對比它們。而這里所反映的情況其實也只是默認值而已,它并不表示此頁面需要緩存。
緩存頁的請求過程
下面再來看一個緩存頁面的請求過程:
對比上一張圖片中可以看出,現在多了【max-age】,【Expires】以及【Last-Modified】這三個響應頭。
這三個頭的含義如下:
1. max-age,Expires:要表達的意思基本差不多。max-age表示某次HTTP的響應結果應該緩存多少秒。
而Expires是說某次HTTP的響應結果應緩存到什么時候過期,此時間是一個UTC時間。
另一個Date頭表示HTPP響應的發出時間,我們可以發現 Date + max-age = Expires
2. Last-Modified:服務端告訴客戶端本次響應返回的HTTP文檔的最后修改時間。這個頭與304的實現有關,后面再來解釋。
分析了HTTP請求過程后,我們再來看一下服務端的頁面是什么樣子的:
注意:上面代碼中最關鍵的一行代碼為:
《%@ OutputCache Duration=“10” VaryByParam=“None” %》
正是由于使用了這個OutputCache指令,最后才會輸出上面那幾個響應頭,用來告訴瀏覽器此頁面需要緩存10秒鐘。
說到這里,可能有些人想有疑惑了:緩存頁在什么時候會起到什么作用呢?
為了演示緩存頁所帶來的現實意義,我將點擊頁面的這些鏈接并以截圖的形式來說明:在一系列請求過程中頁面的顯示情況, 并以頁面的顯示結果來分析緩存所起的作用。
先來看看這個頁面的顯示截圖:
頁面很簡單,主要是顯示了頁面的生成時間與一個刷新鏈接。 從上面提供的頁面代碼,我們應該能知道這個頁面如果是由服務端生成的,則會顯示當前的時間。
不過呢,當我一直(頻繁)點擊【刷新本頁】那個鏈接時,頁面的時間并沒有發生改變,當我發現時間改變時,頁面已顯示成這個樣子了:
由于測試過程中,我一直打開了Fiddler,正好我也把Fiddler監視到的請求結果截圖下來了:
從Fiddler中,我看到FireFox其實只發生了二次請求,但我點擊那個【刷新本頁】起碼超過10次。
以上的這一切,只說明一個事實:如果頁面需要跳轉到某個緩存頁時,且那個緩存頁還沒過期,那么瀏覽器并不會發起到服務器的請求,而是使用緩存頁。
小結:頁面緩存所帶來的好處是:緩存頁面在過期前,用戶通過點擊跳轉鏈接所引發的后續訪問,并不會再次請求服務器。 這對服務器來說可以減少許多訪問次數,因此使用這個特性可以很好地改善程序性能。
緩存頁的服務端編程
前面演示了使用OutputCache指令所產生的緩存頁的效果,由于那些指令需要在頁面的設計階段就寫到頁面上,因此顯得不夠靈活, 不能在運行時調整,雖然ASP.NET也允許我們使用CacheProfile來引入定義在Web.config中的配置,但配置還是沒有運行時的代碼靈活。 我們再來看看如何用代碼來實現上面的效果。
其實用代碼實現緩存頁也很簡單,只需要這樣就可以了:
protected void Page_Load(object sender, EventArgs e)
{
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetExpires(DateTime.Now.AddSeconds(10.0));
}
其實關鍵也就是對Response.Cache的調用。
注意:Response.Cache與我上篇 【細說 ASP.NET Cache 及其高級用法】博客所講的Cache不是一回事,二者完全不相干。
Response.Cache提供:用于設置緩存特定的 HTTP 標頭的方法和用于控制 ASP.NET 頁輸出緩存的方法。
我們還是來說前面的二段示例代碼。可能有些人會想,它們最終的結果真的會是一致的嗎?
要想回答這個問題,我想有必要看一下前面用OutputCache指令的那個頁面最終運行的代碼是個什么樣子的。
在ASP.NET的臨時編譯目錄中,我找到了前面那個文件的一個由ASP.NET處理后的版本:
我們可以看到頁面針對OutputCache指令的設置,最終會調用Page類定義一個方法中:
protected internal virtual void InitOutputCache(OutputCacheParameters cacheSettings)
那個方法實在太長,最終的處理方式也還是在調用this.Response.Cache,有興趣的可以自己去看看那個方法。 至于這個方法的參數為什么是OutputCacheParameters,我想這個容易理解:方便將OutputCache指令的參數全部一起傳入嘛。
所以,也正因為這個緣故,我們也可以直接在代碼中調用Response.Cache的一些方法來實現相同的效果, 由于代碼可以在運行時根據各種參數調整緩存策略,因此會更加靈活,而且可以采用基類的繼承方式來簡化實現。
注意:如果使用OutputCache指令再配合OutputCache Module的使用,可以實現304的效果。
什么是304應答?
通過前面的示例,我們已經看到緩存帶來的好處:那就是可以減少到服務器的訪問,由于不訪問服務器就能顯示頁面,這對于服務器來說, 能減輕一定的訪問壓力。但是,如果用戶強制刷新瀏覽器,那么瀏覽器將會忽略緩存頁,直接向服務器重新發起請求。
也就是說:緩存頁在用戶強制刷新瀏覽器時會無效。
但是,我們之所以使用緩存頁,是因為我們希望告訴瀏覽器:這些數據在一定時間內,并不會發生變化,因此根本不需要再次請求服務器了。 然而,我們不能阻止用戶的行為。由于瀏覽器的重新訪問,我們原來設想的緩存想法將會落空,最后的結果是: 頁面在服務器中重新執行,產生的HTML代碼將重新發送到客戶端。而這一重新刷新的結果可能也是無意義的,因為數據可能根本沒有發生變化, 因此得到的頁面也是不可能有變化的。
再來舉個簡單的例子來說吧:客戶端要瀏覽一張圖片。 當瀏覽器第一次要訪問圖片時,瀏覽器肯定是沒有它的任何緩存記錄的,此時它去訪問服務器,服務器也返回圖片的內容了。 但由于圖片可能會被多個頁面所引用,而它被修改的可能性是很小的。 因此沒有必要為同一瀏覽器的多次請求都去讀取圖片并返回圖片的內容,這樣做既影響性能也學浪費帶寬。 于是,像IIS這樣服務器軟件針對這類靜態文件的訪問時,都會在響應頭上輸出一些標記,用來告之瀏覽器這個文件你可以緩存起來了。
還是回到前面所說的【用戶強制刷新】問題,此時的IIS又會如何處理呢?請看下圖:
注意哦,此時除了HTTP狀態碼變成304之外,沒有任何數據返回哦。
為了讓您對304應答有個深刻的印象,我截了一張狀態碼為200的圖片響應結果:
通過這二張圖片的對比,現在看清楚了吧:304和200并不只是數字上的差別,最重要的差別在于有沒有返回結果。
沒有返回結果,瀏覽器該如何顯示?
您會有這樣的疑慮嗎?
其實不用擔心,此時瀏覽器會使用它緩存版本來顯示。也就是說:不管用戶如何強制刷,服務器就是不返回結果,但仍然可以正常顯示。
顯然,這個效果就是我們想要的。
前面所說的緩存頁遭用戶強刷的問題,如果采用這種方法,就比較完美了。
不過,有一點我要提醒您:Visual Studio自帶的那個WebDev.WebServer.exe不支持304應答,所以您就不要拿它試驗了,不會有結果的。
如何編程實現304應答
前面我們看到了304應答的效果。不過,在ASP.NET中,我們開發的程序,是動態頁面,而不是圖片, 我們更希望某個頁面能以這種方式緩存一段時間,我想這個需求或許會更有意義。
下面,我就來演示如何通過編程的方式實現它。
接下來的示例中,頁面的顯示還是那個樣,顯示頁面在服務器上產生的時間,時間變化了,說明頁面被重新執行了。
重新截一系列的圖片,我認為意義也不大,我就截一張圖片展現多次強刷而產生的過程
上圖反映了我多次請求某個ASPX頁面的過程,從圖片中可以看出,只有第一次是200的響應,后面全是304,是您所期待的結果吧。
再來看看它的實現代碼吧:
雖然代碼并不復雜,但我還是打算來解釋一下:
在瀏覽器第一次請求頁面時,會執行SetLastModified的調用,它會在響應時輸出一個“Last-Modified”這個響應頭, 然后,當瀏覽器再次訪問這個頁面時,會將上次請求所獲取的“Last-Modified”頭的內容 , 以“If-Modified-Since”這個請求頭的形式發給服務端,此時服務器就可以根據具體邏輯來判斷要不要使用304應答了。
在前面的請求圖片的示例中,服務器以圖片文件的最后修改時間做為“Last-Modified”發給瀏覽器, 瀏覽器在后續請求那張圖片時,又以“If-Modified-Since”的形式告之服務端,此時服務端只要再次檢查一下這張圖片就知道圖片在上次訪問后有沒有發生修改, 如果沒有修改,當然就以304的形式告之瀏覽器:繼續使用緩存版本。
還是前面的請求圖片的示例,其實服務端還使用了另一對【請求/響應】頭:
這二個頭的使用方式是:服務端輸出一個ETag頭,瀏覽器在接收后,以If-None-Match的形式在后續請求中發送到服務端, 供服務端判斷是否使用304應答。
“Last-Modified”與“ETag”這二者,事實上只需要使用一個就夠了,關鍵還是看服務端如何處理它們,瀏覽器只是在接收后,下次再發出去而已。
不過,前面的示例代碼并沒有使用緩存頭,事實上,也可以帶上它,這樣可以盡量減少對服務器的訪問,畢竟用戶不會一直強刷瀏覽器。 這二種方式雖然有較大差別,但它們絕對是可以互補的。
為了能形象的描繪緩存頁(或者其它文檔)的請求過程,我畫了張示意圖供大家參考:
如何避開HTTP緩存
前面小節中,介紹了二種方法使用瀏覽器的緩存。但有些時候可能反而希望瀏覽器能放棄它緩存的結果。 現在的瀏覽器都有緩存功能,尤其是對一些靜態文件,比如:圖片,JS,CSS, HTML文件,都能緩存。 但有時候我們需要更新CSS, JS文件呢,瀏覽器如果還使用它的緩存版本,顯然就有問題了。 而且有些網站使用了URL重寫,使原來的動態頁面擴展名也變成靜態的HTML文件了, 因此,仍然希望瀏覽器在某些時候能夠不要緩存這些偽靜態頁面。
此時,我們就希望瀏覽器放棄從HTTP請求所獲得的結果了。 一般說來,瀏覽器在處理(它認為的)靜態文件時,會按照URL為kEY來保存那些緩存結果, 因此,通常的解決辦法也就是修改URL,比如:原來是請求abc.js的,要改成abc.js?t=23434,后面要跟上一個參數, 讓以前的緩存不起作用。至于參數t的取值可以根據文件的最后修改時間,也可以手工指定,總之只要改變它就可以了。
但是,對于偽靜態的頁面,我們不能再使用這種方法了,原因就不用解釋了吧。
那么,可以采用在服務端輸出一個響應頭,通過響應頭的方式告之瀏覽器,不要緩存此文件。 比如,可以調用這個方法:
Response.Cache.SetNoStore();
它會生成這樣的響應頭內容:
Cache-Control: private, no-store
許多瀏覽器都能識別它。還有另一種方法是設置一個已過期的過期時間。
前面所說的在URL中加額外參數的做法,在JS中也比較常用,比如 JQuery就支持讓某個Ajax請求不緩存, 它的方式就是設置{cache: false},最終它便會在生成的URL中加上一個臨時參數,以保證后面的請求的地址是不重復的, 最終達到避開緩存的目的。JQuery的使用太簡單,我就不再給出示例代碼了。
-
HTTP
+關注
關注
0文章
511瀏覽量
31519 -
ASP
+關注
關注
0文章
98瀏覽量
34116 -
瀏覽器
+關注
關注
1文章
1036瀏覽量
35535
發布評論請先 登錄
相關推薦
E2000 Speedometer測試瀏覽器性能
如何調試 HTTP 請求和響應
Web緩存的類型及功能分析
AWTK 最新動態:支持瀏覽器控件
![AWTK 最新動態:支持<b class='flag-5'>瀏覽器</b>控件](https://file.elecfans.com/web2/M00/50/DA/pYYBAGLH6TyAB71EAAAPQ7KgtYA038.png)
![](https://file1.elecfans.com/web2/M00/05/31/wKgaombNFN2ATwRhAAMx7bUXWjo012.jpg)
不只是前端,后端、產品和測試也需要了解的瀏覽器知識(二)
![不只是前端,后端、產品和測試也需要了解的<b class='flag-5'>瀏覽器</b>知識(二)](https://file1.elecfans.com//web2/M00/02/02/wKgZoma5rGWAFdf0AAIbzLRvuBs118.png)
評論