asp.net
什麼是ViewState?
對於ViewState,我們有許多的誤解。Viewstate不儲存控制項,而是去儲存form中對應ID控制項的值,特別是那些由於他們沒有和form一起post 而在頁面回傳時會丟失的控制項的值。viewstate一般不要用來儲存session或在頁面間傳輸資料。在頁面回傳後,viewstate不能用來動態地建立頁面的控制項。他在頁面回傳之後不回複控制項的值。甚至一個控制項的viewstate被禁止了,在頁面回傳後,控制項的值仍然不會丟失,比如textbox,dropdownlist控制項。那什麼是viewstate呢?viewstate儲存最後一次在伺服器上處理的頁面狀態。他不能儲存那些被動態改變的控制項的值。
viewstate是如何工作的?
所有的伺服器端控制項都有一個ViewState屬性。如果他是enable的,這個控制項的viewstate就起作用了。那viewstate是在哪裡,是如何儲存的呢?當一個頁面第一次載入,所有的控制項被序列化到viewstate,儲存在一個叫_ViewState的隱藏form欄位裡。這個隱藏欄位對應伺服器端的ViewState對象。頁面的ViewState使用System.Web.UI.StateBagObject Storage Service索引值對。當一個回傳發生,頁面還原序列化ViewState然後恢複所有的控制項。頁面中儲存控制項的ViewState以base 64 編碼格式儲存成name - value。當一個頁面重新載入,會調用兩個和ViewState相關的方法, LoadViewState 和SaveViewState。下面是我的一個頁面中的_ViewState隱藏欄位。
<input type="hidden" name="__VIEWSTATE" value="dNrATo45Tm5QzQ7Oz8AblWpxPjE9MMl0Aq765QnCmP2TQ==" />
啟用和禁止ViewState
在預設情況下,所有伺服器控制項的viewstate開啟狀態,通過以及幾種途徑來禁止。
1.頁面層級
2.控制項層級
3.應用程式層級
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能處理以下的類型
基本類型,基本類型數組,ArrayList 和Hashtable,任何可以序列化的對象。
以下代碼是將ArrayList存到viewstate中並取出
ArrayList obj = new ArrayList();
//Some code
ViewState["ViewStateObject"] = obj;
obj = ViewState["ViewStateObject"];
效能問題
為了更好的頁面呈現效能,viewstate應該儘可能的小。要記住Viewstate中的資料會佔用很多的網路頻寬。因此我們要謹慎的利用viewstate。如果頁面和控制項不需要回傳,那麼就要禁止viewstate屬性。通常在aspx頁面之外儲存Viewstate會取得更好的效能表現。為了達到這個目的,我們可以使用SavePageStateToPersistenceMedium 和LoadPageStateFromPersistenceMedium 這兩個方法。在web.config或machine.config設定來禁止某個程式的所有頁面或全部程式頁面的viewstate。
注意只有控制項包含在<form runat=server>裡才能儲存viewstate。然而即使頁面所有的viewstate被禁止,頁面仍然在viewstate中儲存20位元組的資料,用來在回傳時為相應的控制項分配viewstate中的資料。所以當頁面完全沒有回傳,移去runat="server"能減少20位元組的資料。如果有很多這樣的頁面,20位元組的節省也能在一定程度上減少頻寬。viewstate應該在必要的時候使用。在DataGrid和DataRepeater這樣的控制項中要避免使用viewstate,因為這些控制項的viewstate佔用資料相當大。
下面我提供一個簡單的用於計算頁面viewstate大小的方法。建立一個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中的內容加密
EnableViewStateMac會進行一個機器授權驗證(MAC),這應在頁面層級或程式層級使用。當設定時,這個屬性會在viewstate呈現之前附加一個viewstate的hash值。當回傳發生時,hash值會被重新計算和核對。如果他們不匹配,頁面會拒絕顯示,這樣就確保了viewstate沒有被纂改。
在machine.config中設定對viewstate內容的加密
<machineKey validation="3Des" /> or <machineKey validation="SHA1"/>
viewstate容易出錯的地方
當將一個頁面的控制項傳輸到另外一個頁面(第二個頁面)時,通常會出現錯誤。解決方案是在第二個頁面中將viewstate禁用。