ASP.NET的ViewState的取捨

對於大多數程序員而言,viewstate就是頁面中的名字叫做__VIEWSTATE的隱藏控件,它使我們頁面體積膨脹,不利於頁面的seo優化;頁面返送間都要攜帶更多的大量的數據,耗費更多的流量、延長響應時間。它使得我們很焦慮:禁用它,甚至不再使用web form,轉向MVC,在MVC還沒推出之前,甚至是轉向其它的平臺。。。

 

先不說那種取捨的對錯,自己還是很喜歡web form這套框架的,看看頁面的生命週期,它把整個頁面的創建工業化、流水線化,它能讓我們很容易的造一個”航母”出來,相比來說mvc就像一個手工作坊。但是對於web這種輕量級的應用,mvc顯然更加適合,web form反而有點殺雞牛刀的感覺。

 

如果你也同樣喜歡web form,你必須對讓web form超神,同時也可能超鬼的viewstate相當的瞭解。關於如何超鬼,開頭已經說了;如何超神的呢,我們都知道http是一種無狀態的協議,也就是我們的每次請求實際上都有創建一個網頁的新實例,viewstate這個一級技能就能讓頁面滿狀態原地復活(豈能不超神。。。),從而節省一些數據讀取時間和資源,其次它不像session、cache那像持續佔用服務器內存,在頁面實例創建完畢之後,就會被清理,節省服務器資源。

 

viewstate的前世今生

 

從MSDN中我們可以知道一個頁面生命週期大約可分爲爲:頁請求、開始、初始化、加載、驗證、回發事件處理、呈現、卸載這幾個階段。

引用MSDN的一個圖:

IC481543

 

1,我們可以看到在 OnInit 和 OnInitComplete方法之間有一個TrackViewSate的方法,它開啓對視圖狀態更改的跟蹤。也就是說,從這裏開始,你才能使用viewstate(否則這些數據將不會被髮送回頁面),頁面開始建立所有控件狀態的一個快照。

2,加載階段OnPreLoad之前的LoadViewState方法,如果頁面是回發的,他就會使用頁面回發的viewstate,利用它記錄的信息改變對應控件的屬性,讓你頁面原狀態復活。

3,在頁面呈現之前的預呈現階段,我們會看到一個SaveViewState的方法,在這裏它會檢查頁面的所有控件的屬性,如果和之前建立的快照有不一樣的,就會記錄下來。在此之後的控件的屬性修改也不會被記錄下來,回送到頁面。

4,最後這些記錄的信息會序列化爲Base64編碼的字符串,做爲一個隱藏的字段的值發回到頁面中。

 

合理使用viewstate

 

知道了viewstate怎麼來的,我們就可以什麼時候使用viewstate,怎麼避免產生viewstate。

1,如果控件的值就不會發生變化,可以靜態文本出現,不要在程序裏爲它賦值。

2,如果一些控件需要回發中被重新填充數據或值會根據用戶的輸入而改變的,都沒必要使用。

3,如果一個頁面根本不存在回發或者一個簡單的回發可以使用其它ajax等去實現的,果斷禁用此頁的viewstate。

 

如何禁用viewstate,這個簡單,就是在web.config或頁面上加一個EnableViewState設爲false即可。

有點注意的是,你如果要只對頁面的某個控件禁用viewstate,你可以對頁面開啓EnableViewState而只對這個控件設置EnableViewState禁用就OK了;

但是如果你只對某個控件使用viewstate,對頁面EnableViewState禁用和控件EnableViewState開啓是達不到效果的。

因爲頁面的EnableViewState=”false”會強制的把整個頁面及其控件的禁用。

想要達到這種效果,我們需要用到另一個ViewStateMode的屬性,我們要對頁面設置EnableViewState=”true”,然後通過ViewStateMode=“Disabled”來禁用頁面的viewstate,而控件默認的ViewStateMode=“Inherit”,我們再去設置需要啓用viewstate控件的ViewStateMode=“Enabled”來只啓用某個控件使用viewstate。

但是這裏還有一點我們要注意的是,我們做一個試驗,我們新建一個頁面,頁面很簡單就放幾個asp:Label,並且在Page_Load中給這几上label賦值,並且給設置頁面的Trace=”true”,也就是開啓頁面的跟蹤。

第一次設置頁面 EnableViewState=”true”,瀏覽,首先頁面源代碼,可以看到頁面id=”__VIEWSTATE” 的input有了一大段的數據,在頁面的跟蹤信息的ControlTree表格上,可以看到每個控件是viewstate佔用了多少空間。

image

 

第二次我們設置頁面 EnableViewState=”false”,結果一定想得到,頁面代碼中id=”__VIEWSTATE” 的input雖然還有數據,但明顯少了很多,頁面的跟蹤信息中數據也發生了變化就是各個控件的viewstate已經是0了。

image

 

最後設置EnableViewState=”true” ,但是同時設置頁面ViewStateMode=”Disabled”,並且還設置頁面中一個label的 ViewStateMode=”Disabled”,這樣其實也可以起到禁用頁面viewstate的作用,瀏覽再看一下結果:頁面代碼中id=”__VIEWSTATE” 的input還是很少的一點,就像是設置EnableViewState=”false”一樣,但是對於跟蹤的信息呢?

image

這個數據卻又像只是設置了EnableViewState=”true”一樣,細心的童鞋可能會發現其實有點不一樣那就是表格的最頂行的_page的viewstate沒有佔用空間,但是label的空間依然是有的,儘管我們設置了其中的一個ViewStateMode=”Disabled”。

 

通過這個小試驗,我們可以發現ViewStateMode=”Disabled”不會使得頁面流程中停止快照的建立和預呈現的對比,並且記錄這個信息,它只是沒有把這些個信息發送到頁面,也就是說它雖然節省了往返帶寬,但是服務器其實還是進行了整個viewstate信息的流程的,只是在最後一步將其遺棄。

 

安全性

 

首先一點是我們可以在web.config中設置pages的maxPageStateFieldLength來設置單個viewstate的最大值,以防止一些代理、防火牆或移動瀏覽器因爲一個隱藏字段體積過大而阻止頁面。

 

其次再說的,我們頁面中看到的viewstate字符串,其實是沒有加密的,只是經過了Base64的編碼,我們可以很easy的逆向出來看到原始的數據。不過還好的是,asp.net默認啓用了一個基於散列碼來保證viewstate不被修改:asp.net會根據消息驗證代碼 (MAC) 密鑰去計算viewstate,得到一個散列碼,然後把這個碼添加到viewstate中,保存到頁面的隱藏字段中;如果頁面回發時,再次計算散列碼與之前的比較,如果不匹配就拒絕接受此次回發。

默認情況下,這個散列的密鑰是根據計算機的MAC地址計算的,如果在web場中,就可能會不能驗證viewstate。這種環境下,我們可以通過EnableViewStateMac=”false”來禁用校驗;另一種方法就是在多臺服務器間配置同樣的自己生成的密鑰,然後分佈在不同的計算機的machine.config中配置。

 

即使這樣,我們viewstate還是很容易別人看到,如果不想這樣,我們可以用ViewStateEncryptionMode來加密viewstate,它有三個值:Always,總是加密;Never,從不加密;Auto,僅在需要時加密,在呈現Html之前顯式調用Page.RegisterRequiresViewStateEncryption()來啓用加密,否則不加密。

 

結語

 

不同的場景,有不同的需求,只有充分的瞭解才能讓我們在使用上合理取捨,取長避短。

 

轉載自:http://www.cnblogs.com/forcertain/archive/2012/03/04/2379719.html

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