什么是ViewState?
對于ViewState,我們有許多的誤解。Viewstate不保存控件,而是去保存form中對應(yīng)ID控件的值,特別是那些由于他們沒有和form一起post 而在頁面回傳時會丟失的控件的值。viewstate一般不要用來保存session或在頁面間傳輸數(shù)據(jù)。在頁面回傳后,viewstate不能用來動態(tài)地創(chuàng)建頁面的控件。他在頁面回傳之后不回復(fù)控件的值。甚至一個控件的viewstate被禁止了,在頁面回傳后,控件的值仍然不會丟失,比如textbox,dropdownlist控件。那什么是viewstate呢?viewstate保存最后一次在服務(wù)器上處理的頁面狀態(tài)。他不能保存那些被動態(tài)改變的控件的值。
viewstate是如何工作的?
所有的服務(wù)器端控件都有一個ViewState屬性。如果他是enable的,這個控件的viewstate就起作用了。那viewstate是在哪里,是如何存儲的呢?當(dāng)一個頁面第一次加載,所有的控件被序列化到viewstate,保存在一個叫_ViewState的隱藏form字段里。這個隱藏字段對應(yīng)服務(wù)器端的ViewState對象。頁面的ViewState使用System.Web.UI.StateBag對象存儲鍵值對。當(dāng)一個回傳發(fā)生,頁面反序列化ViewState然后恢復(fù)所有的控件。頁面中保存控件的ViewState以base 64 編碼格式存儲成name - value。當(dāng)一個頁面重新加載,會調(diào)用兩個和ViewState相關(guān)的方法, LoadViewState 和SaveViewState。下面是我的一個頁面中的_ViewState隱藏字段。
<input type="hidden" name="__VIEWSTATE" value="dNrATo45Tm5QzQ7Oz8AblWpxPjE9MMl0Aq765QnCmP2TQ==" />
啟用和禁止ViewState
在默認(rèn)情況下,所有服務(wù)器控件的viewstate開啟狀態(tài),通過以及幾種途徑來禁止。
1.頁面級別
2.控件級別
3.應(yīng)用程序級別
4.機器級別
頁面級別禁止的方法是在頁面的開始寫入
<%@ Page EnableViewState ="False" %>
or
<%@ Page EnableViewState ="True" %>
控件級別是
<asp:TextBox id="txtCode" runat="server” EnableViewState="false" />
or
<asp:TextBox id="txtCode" runat="server" EnableViewState="true" />
程序級別是在web.config中
<pages enableViewState="false" />
or
<pages enableViewState="true" />
機器級別是在machine.config中
<pages enableViewState="true" enableViewStateMac="true" ... />
or
<pages enableViewState="false" ... />
在viewstate中保存和取出值
viewstate能處理以下的類型
基本類型,基本類型數(shù)組,ArrayList 和Hashtable,任何可以序列化的對象。
以下代碼是將ArrayList存到viewstate中并取出
ArrayList obj = new ArrayList();
//Some code
ViewState["ViewStateObject"] = obj;
obj = ViewState["ViewStateObject"];
性能問題
為了更好的頁面呈現(xiàn)性能,viewstate應(yīng)該盡可能的小。要記住Viewstate中的數(shù)據(jù)會占用很多的網(wǎng)絡(luò)帶寬。因此我們要謹(jǐn)慎的利用viewstate。如果頁面和控件不需要回傳,那么就要禁止viewstate屬性。通常在aspx頁面之外保存Viewstate會取得更好的性能表現(xiàn)。為了達(dá)到這個目的,我們可以使用SavePageStateToPersistenceMedium 和LoadPageStateFromPersistenceMedium 這兩個方法。在web.config或machine.config設(shè)置來禁止某個程序的所有頁面或全部程序頁面的viewstate。
注意只有控件包含在<form runat=server>里才能存儲viewstate。然而即使頁面所有的viewstate被禁止,頁面仍然在viewstate中保存20字節(jié)的數(shù)據(jù),用來在回傳時為相應(yīng)的控件分配viewstate中的數(shù)據(jù)。所以當(dāng)頁面完全沒有回傳,移去runat="server"能減少20字節(jié)的數(shù)據(jù)。如果有很多這樣的頁面,20字節(jié)的節(jié)省也能在一定程度上減少帶寬。viewstate應(yīng)該在必要的時候使用。在DataGrid和DataRepeater這樣的控件中要避免使用viewstate,因為這些控件的viewstate占用數(shù)據(jù)相當(dāng)大。
下面我提供一個簡單的用于計算頁面viewstate大小的方法。創(chuàng)建一個MasterPageBase 類,然后其他所有的頁面都要繼承他。
public class MasterPageBase: System.Web.UI.Page
...{
protected override void OnPreRender(EventArgs e)
...{
object viewStateObject = HttpContext.Current.Request["__VIEWSTATE"];
if (viewStateObject == null)
HttpContext.Current.Trace.Warn("The ViewState Size is:", "0");
else
HttpContext.Current.Trace.Warn("The ViewState Size is:",
HttpContext.Current.Request["__VIEWSTATE"].Length.ToString());
base.OnPreRender(e);
}
}
安全問題
可以采取兩個措施來避免viewstate被仿冒
使用EnableViewStateMac屬性
給 ViewState中的內(nèi)容加密
EnableViewStateMac會進(jìn)行一個機器授權(quán)驗證(MAC),這應(yīng)在頁面級別或程序級別使用。當(dāng)設(shè)置時,這個屬性會在viewstate呈現(xiàn)之前附加一個viewstate的hash值。當(dāng)回傳發(fā)生時,hash值會被重新計算和核對。如果他們不匹配,頁面會拒絕顯示,這樣就確保了viewstate沒有被纂改。
在machine.config中設(shè)置對viewstate內(nèi)容的加密
<machineKey validation="3Des" /> or <machineKey validation="SHA1"/>
viewstate容易出錯的地方
當(dāng)將一個頁面的控件傳輸?shù)搅硗庖粋€頁面(第二個頁面)時,通常會出現(xiàn)錯誤。解決方法是在第二個頁面中將viewstate禁用。