程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> ViewState機制由淺入深3

ViewState機制由淺入深3

編輯:關於.NET

2.2.4  StateBag類

ViewState是控件的一個屬性,用來使用控件具有記憶功能。在前邊的講述中,我們可以看到控件的一些屬性通過使用ViewState能夠恢復原來的值,保存本次的值,在Control類中很多方法的實現也是直接調用了ViewState的方法。ViewState的類型是StateBag,下面我們就了解一下在StateBag中是如何實現這些功能的。StateBag定義在System.Web.UI中聲明如下:

public sealed class StateBag : IStateManager, IDictionary, ICollection, IEnumerable

StateBag類可以理解為是一個具有狀態管理功能的字典,因為它實現了IStateManager, IDictionary 這兩個接口。StateBag類可以象字典那樣保存Key/Value對,其中Key是字符串而Value是對象。下面是一個使用StateBag的例子。

  protected void Button2_Click(object sender, EventArgs e)
  {
    StateBag TestSB = new StateBag();
    TestSB["b"] = "bbbbb";
    TextBox1.Text = TestSB["b"].ToString();
}

在上面的例子中使用StateBag保存一個Key為“b”,其值為“bbbbb”的Key/Value對。ViewState屬性也是StateBag的一個實例,當然也就可以象上面那樣使用。在ViewState中保存了很多的Key/Value對(鍵值對),這些Key/Value對用來保存控件的屬性,這些Key/Value對是有ASP.Net來維護的。當然我們也可以增加一些自己的Key/Value對,來保存一些信息。

StateBag還實現System.Web.UI.IStateManager接口,這樣它具有狀態管理功能。下面對StateBag如何提供狀態管理功能進行說明。

1)   StateItem類

StateBag中保存Key/Value對,Key是String類型,Value是Object類型。但是在StateBag內部保存Value不是Object類型,而是將Object類型轉換為StateItem類型然後保存,從StateBag中取出的時候再將StateItem類型轉換為Object類型,也就是說StateBag中的Key/Value對實際上是String/StateItem類型。轉換過程是在StateBag內部實現客戶感覺不到。StateItem的聲明如下:

public sealed class StateItem
{
  internal StateItem(object initialValue);
  public bool IsDirty { get; set; }
  public object Value { get; set; }
}

通過上面的代碼我們可以看出實際上多了一個IsDirty屬性,來標記當前的Value是否已經被修改過。

2)   Add

Add方法是將傳入的Key,Value保存到字典中,並處理IsDirty屬性。在StateBag.LoadViewState方法中會調用Add方法。其示意代碼如下:

public StateItem Add(string key, object value)
{
  StateItem item = this.bag[key] as StateItem;
  if (item == null)
  {
    if ((value != null) || this.marked)
    {
      item = new StateItem(value);
      this.bag.Add(key, item);
    }
  }
  else
  {
    item.Value = value;
  }
  if ((item != null) && this.marked)
  {
    item.IsDirty = true;
  }
  return item;
}

雖然函數的名稱是Add,其實也包括了更新。如果當前的項在字典中不存在則新增,否則更新。新增時新建一個StateItem類型的對象item,將Key和item增加到字典中。如果Item不為null,並且跟蹤狀態標記為true,則item的IsDirty為true。什麼情況下會調用Add方法呢?主要有兩種情形一種是StateBag.LoadViewState,在下面會具體介紹到。還有一種情況就是對控件的屬性賦值的時候,比如Button.Text=”button”,此時會調用Text屬性的Set,在Set中執行的代碼this.ViewState["Text"] = value,這個代碼實際上執行this.ViewState.Add("Text",value)。

3)   LoadViewState

還原以前保存的ViewState。將傳入的ArrayList對象加載到字典中。示意代碼如下:

internal void LoadViewState(object state)
{
    ArrayList list = (ArrayList) state;
    for (int i = 0; i < list.Count; i += 2)
    {
      string key = ((IndexedString) list[i]).Value;
      object obj2 = list[i + 1];
      this.Add(key, obj2);
    }
  }

我們知道在初始化階段StateBag.TrackViewState都已經被調用過了,也就是說marked為true了,這樣在StateBag.LoadViewState中調用Add方法第一次加載完ViewState的數據後,所有的StateItem的IsDirty屬性都是true。

4) SaveViewState

將字典中已經修改過的Key/Value存放在一個ArrayList對象中返回。

internal object SaveViewState()
{
  ArrayList list = null;
  IDictionaryEnumerator enumerator = this.bag.GetEnumerator();
  while (enumerator.MoveNext())
  {
      StateItem item = (StateItem) enumerator.Value;
      if (item.IsDirty)
      {
        list.Add(new IndexedString((string) enumerator.Key));
        list.Add(item.Value);
      }
    }
  }
  return list;
  }

在SaveViewState中只會將字典中item.IsDirty=true的項目返回,哪些項的IsDirty=true呢?通過上面對Add方法及LoadViewState的分析我們知道,當我們第一次設置了控件的某個屬性後會調用Add方法,這個屬性對應的StateItem的屬性IsDirty會被設置為true,在SaveViewState時會保存這個item。在下次請求時在LoadViewState中也會將IsDirty設置為true,在SaveViewState是會保存這個item。總之只要控件屬性的被修改過和默認值不一致都會一直被保存,即使這個屬性的值僅僅被修改過一次,之後保存不不變,也會在多次PostBack之間保存起來。

5)   TrackViewState

在TrackViewState方法中,設置跟蹤標記為True,其目的就是開始狀態跟蹤了。示意代碼如下:

internal void TrackViewState()
{
  this.marked = true;
}

3 ViewState與ControlState

ControlState是一個自定義的狀態保持機制,也就是說保持狀態的機制需要開發人員自己去完成,而不像ViewState,它有自己默認的狀態保持機制。既然已經有了ViewState為什麼還需要ControlState呢?因為ViewState是可以被禁用的,而ControlState卻不能被禁用,對於有些必須保存的信息,就可以使用ControlState。ControlState的實現思路基本上與ViewState類似,ControlState需要保存的信息也被序列化後保存在__VIEWSTATE中。使用ControlState,需要在OnInit 方法中調用 RegisterRequiresControlState向頁面注冊,而且要重寫SaveAdapterControlState,LoadAdapterControlState這兩個方法自己,實現要保存什麼,怎樣保存。

4 ViewState的使用

4.1ViewState的優缺點

使用ViewState首先要了解ViewState與其他的保持狀態機制相比有什麼優缺點。

使用ViewState具有以下優點:

一、耗費的服務器資源較少(與Application、Session相比)。因為,視圖狀態數據都寫入了客戶端計算機中。

二、易於維護。默認情況下,DotNet系統自動啟用對狀態數據的維護。

三、因為它不使用服務器資源、不會超時,並且適用於任何浏覽器。

使用視圖狀態具有以下缺點:

一、性能問題。由於視圖狀態存儲在頁本身,因此如果存儲較大的值,用戶顯示頁和發送頁時的速度仍然可能減慢。ViewState 增加了發送到浏覽器的頁面的大小,同時也增加了回傳的窗體的大小,因此不適合存儲大量數據

二、設備限制。移動設備可能沒有足夠的內存容量來存儲大量的視圖狀態數據。

三、潛在的安全風險。視圖狀態存儲在頁上的一個或多個隱藏域中。雖然視圖狀態以哈希格式存儲數據,但它可以被篡改。如果直接查看頁輸出源,可以看到隱藏域中的信息,盡管 ViewState 數據已被編碼,並且可以選擇對其進行加密,但始終不將數據發送到客戶端才是最安全的。

4.2ViewState安全性

ViewState將一些信息保存在客戶端,而且默認情況下客戶端的數據僅僅是進行了Base64編碼了,很容易被看到原文。當然ViewState還有一些安全性措施來改善這一點。ViewState 安全性對於處理和呈現 ASP.NET 頁面所需的時間有直接的影響。簡單地說,安全性越高,速度越慢。因此如果不需要,不要為 ViewState 添加安全性。

4.2.1  MAC

MAC是message authentication check的簡寫即消息驗證檢查。MAC可以防止ViewState數據被篡改。可以通過設置EnableViewStateMAC="true"屬性來啟用消息驗證檢查。可以在頁面級別上設置 EnableViewStateMAC,也可以在應用程序級別上設置。有一點需要特別說明的是在MSDN中說EnableViewStateMAC的默認值是Flase,但是實際上發現EnableViewStateMAC的默認值是True,這一點大家可以Page_Load中輸出EnableViewStateMAC屬性來驗證。也就是說默認情況下都是對ViewState進行防篡改處理的。下面的描述結合“2.2.1Page中的處理”中的“4)ViewState序列化與反序列化”進行理解。

在ObjectStateFormatter中進行序列化的時候如果EnableViewStateMAC="true",則在MachineKeySection.GetEncodedData中根據字節流buf計算出一個20位的字節表示Hash值,增加在buf字節流的後邊,形成一個新的字節流。

在ObjectStateFormatter中進行反序列化的時候如果EnableViewStateMAC="true",則在MachineKeySection.GetDecodedData中取buf的前(length-20)位計算出一個20位的字節表示Hash值,將這個Hash值與buf的最後20位進行比較,如果不一直則說明ViewState被篡改了則拋出異常。

默認情況下,.NET框架使用SHA1算法來生成ViewState散列代碼。此外,也可以通過在machine.config文件中設置<machineKey>來選擇 MD5 算法,如下所示:<machineKey validation="MD5" />。MD5算法的性能要比SHA1算法好一些,但是同樣不夠安全。

4.2.2加密

默認情況下__VIEWSTATE中存儲的值僅僅僅進行了Base64編碼,並沒有進行加密,如果ViewState中有一些敏感信息需要增加安全性,我們也可以對ViewState進行加密。我們可以設置ViewStateEncryptionMode的值來決定是否加密,其值是“Auto”,“Always”,“Never”,默認值是“Auto”。“Always”表示進行加密,“Never”表示不進行加密,“Auto”時如果調用了RegisterRequiresViewStateEncryption方法後則進行加密。ViewStateEncryptionMode屬性不能在代碼中設置page指令或者配置文件中使用。

4.2.3設置ViewStateUserKey

設置 ViewStateUserKey 屬性有助於防止您的應用程序受到惡意用戶的點擊式攻擊。必須在頁處理的 Page_Init 階段設置此屬性。具體的信息可以MSDN中的說明,鏈接如下:

http://msdn2.microsoft.com/zh-cn/library/ms972969.aspx

4.3ViewState的禁用

因為ViewState會一定程度上影響性能所以在不需要的時候禁用 ViewState。默認情況下 ViewState 將被啟用,並且是由每個控件(而非頁面開發人員)來決定存儲在 ViewState 中的內容。有時,這一信息對應用程序並沒有什麼用處。盡管也沒什麼害處,但卻會明顯增加發送到浏覽器的頁面的大小。因此如果不需要使用 ViewState,最好還是將它關閉,特別是當 ViewState 很大的時候。通過將對象的EnableViewState屬性設置為False禁用ViewState。可以針對單個控件、整個頁面或整個應用程序禁用ViewState,如下所示:

每個控件(在標記上)<asp:datagrid EnableViewState="false" ?/>

每個頁面(在指令中) <%@ Page EnableViewState="False" ?%>

每個應用程序(在 web.config 中) <Pages EnableViewState="false" ?/>

以下情況將不再需要ViewState:(1)控件未定義服務器端事件(這時的控件事件均為客戶端事件且不參加回送的);(2)控件沒有動態的或數據綁定的屬性值。

4.4ViewState優化

ViewState優缺點並存,有些人支持使用,有些人反對使用,也有人提出了對ViewState的優化,主要是壓縮ViewState減少傳輸量及修改ViewState的存儲位置。具體可以參考一下的鏈接。

壓縮:http://www.cnblogs.com/mack/archive/2005/07/27/201411.html

修改存儲位置:http://czhenq.cnblogs.com/archive/2006/04/03/365807.html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved