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