ViewState机制的解析(转自csdn)
1???????? ViewState機制是什么?
ViewState機制是asp.net中對同一個Page的多次請求(PostBack)之間維持Page及控件狀態的一種機制。在WebForm中每次請求完,Page對象都會被釋放,對同一個Page的多次請求之間的狀態信息,如何進行維護呢?WebForm中,每次請求都會存在客戶端和服務器之間的一個交互。如果請求完成之后將一些信息傳回到客戶端,下次請求的時候客戶端再將這些狀態信息提交給服務器,服務器端對這些信息使用和處理,再將這些信息傳回給客戶端。這樣是不是就可以對同一個Page的多次請求(PostBack)之間維持狀態了。對這就是ViewState的基本工作模式。ViewState的設計目的主要就是為了將必要的信息持久化在頁面中。這樣通過ViewState在頁面回傳的過程中保存狀態值,使原本沒有“記憶”的Http協議變得有“記憶”起來。
2???????? ViewState機制如何工作?
下面我們看看ViewState機制是如何具體的工作的。
2.1?客戶端:
我們先從客戶端看起,在客戶端的HTML源代碼中我們可以看到下面的代碼
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwULLTE0MTAzNDUwNThkZKr77J2uy7fatyBou8PocG80X4Jt" />
這個就是ViewState在客戶端的保存形式,它保存在一個ID為__VIEWSTATE的Hidden中,它的Value是使用Base64編碼后的字符串。這個字符串實際上是一個對象(Pair類型)序列化之后的結果。這個對象保存了整個頁面的控件樹的ViewState。可以使用一些工具將這個字符串進行解碼查看其內容,比如ViewStateDecoder,ViewStateAnalyzer。
2.2?服務器端:
在服務器端和ViewState機制密切相關的有三個類Page,Control,StateBag。他們3者的關系如下圖所示:
圖1
Page繼承自Control,Control和StateBag是聚合關系,在Control中有一個StateBag的實例ViewState。這三個類互相協作完成ViewState機制的大概過程如下。Page對客戶端請求進行處理,在處理的過程中先是將客戶端提交的_VIEWSTATE反序列化為對象,調用Control的相關方法給所有的控件裝載數據,這些數據是上次請求結束后控件的狀態數據。在之后的一些事件中這些狀態數據可能被修改。在請求結束之前調用Control的相關方法得到所有控件的被修改過的狀態數據,之后Page將其進行序列化,并返回給客戶端。在Control中又具體調用StateBag類的方法完成狀態數據的加載和保存。
2.2.1?? Page中的處理
圖2 Page生命周期
1)???? InitRecursive
在Page的生命周期中有3處與ViewState相關,在初始化階段調用Control. InitRecursive,它遞歸對所有的控件進行初始化,其中調用了Control.TrackViewState。TrackViewState中打開跟蹤ViewState開關。
2)???? LoadAllState
在初始化完成之后會調用Page.LoadAllState,LoadAllState只有在PostBack的時候才會執行,它的主要功能是將從頁面傳遞來的__VIEWSTATE的值反序列化為Pair類型的對象,然后將這個對象中存儲的ViewState的值加載到Page及所有控件中。實際上LoadAllState加載了ControlState(控件狀態)及ViewState(視圖狀態),本文主要是討論ViewState,對ControlState部分的處理不進行描述。
LoadAllState中主要有兩步:Page.LoadPageStateFromPersistenceMedium和Control.LoadViewStateRecursive。
在LoadPageStateFromPersistenceMedium中發生了如下的調用層次
Page.LoadPageStateFromPersistenceMedium
è??? HiddenFieldPageStatePersister.Load
è??? ObjectStateFormatter.Deserialize
以上完成的功能是將客戶端提交的_VIEWSTATE反序列化為一個類型為Pair的對象pair。
Control.LoadViewStateRecursive中將遞歸加載控件的ViewState,具體在下面進行講解。
3)???? SaveAllState
SaveAllState它的操作和LoadAllState相反。SaveAllState中主要有兩步Control.SaveViewStateRecursive及Page SavePageStateToPersistenceMedium。
Control.SaveViewStateRecursive中將所有控件的ViewState屬性遞歸加載到一個Pair對象中,具體實現細節在下面講解。
Page SavePageStateToPersistenceMedium中發生如下的調用關系。
Page SavePageStateToPersistenceMedium
è??? HiddenFieldPageStatePersister.Save
è??? ObjectStateFormatter.Serialize
將Control.SaveViewStateRecursive生成的對象序列化為一個字符串,并賦值給Page.ClientState屬性。
在Render階段發生如下的調用關系:
HtmlForm.RenderChildren
è??? Page.BeginFormRender
è??? Page.RenderViewStateFields
最終將ClientState屬性中的值寫入到HTML頁面的_VIEWSTATE中。
在Control.InitRecursive中打開跟蹤開關,打算對ViewState的值進行跟蹤,在Page.LoadAllState中將客戶端提交的__VIEWSTATE的值裝載到各個控件的ViewState中,在Page.SaveAllState中將發生變化的ViewState序列化為一個字符串,在Render階段發送回客戶端。
4)???? ViewState序列化與反序列化
PageStatePersister 是一個抽象類,是表示將ViewState信息序列化及反序列化機制的基類。在Page.LoadPageStateFromPersistenceMedium中示意代碼如下:
protected internal virtual object LoadPageStateFromPersistenceMedium()
{
????????? PageStatePersister pageStatePersister = this.PageStatePersister;
????????? pageStatePersister.Load();
????????? return new Pair(pageStatePersister.ControlState, pageStatePersister.ViewState);
}
在Page.SavePageStateToPersistenceMedium中的示意代碼如下:
protected internal virtual void SavePageStateToPersistenceMedium(object state)
{
??? ??? PageStatePersister pageStatePersister = this.PageStatePersister;
??????? Pair pair = (Pair) state;
??????? pageStatePersister.ControlState = pair.First;
??????? pageStatePersister.ViewState = pair.Second;
??????? pageStatePersister.Save();
}
在Asp.net2.0中實現PageStatePersister這個抽象類,具體提供持久化機制的類是HiddenFieldPageStatePersister。它實現了Load和Save兩個方法,Load時將__VIEWSTATE反序列化為一個Pair對象,Save時將Pair對象序列化為一個字符串賦值給Page.ClientState。HiddenFieldPageStatePersister中采用的格式器是ObjectStateFormatter,其實現string Serialize(object state),和object Deserialize(string serializedState)這兩個方法,從而實現對Pair對象的序列化和反序列化。
public string Serialize(object state)中的示意代碼如下:
MemoryStream memoryStream = GetMemoryStream();
Serialize(memoryStream, state);
byte[] buf = memoryStream.GetBuffer();
if (RequiresViewStateEncryptionInternal)
{
?buf = MachineKeySection.EncryptOrDecryptData(true, buf, this.GetMacKeyModifier(), 0, length);
?length = buf.Length;
}
else if (EnableViewStateMac)
{
buf = MachineKeySection.GetEncodedData(buf, this.GetMacKeyModifier(), 0, ref length);
}
return Convert.ToBase64String(buf, 0, length);
將state序列化為內存流,在將其轉換為字節流。如果需要加密則對其進行加密處理,否則需要Mac則進行Mac處理。最后將字節流進行Base64編碼轉換為字符串。
public object Deserialize(string serializedState) 中的示意代碼如下:
byte[] buf = Convert.FromBase64String(serializedState);
int length = buf.Length;
if (ContainsEncryptedViewState)
{
buf = MachineKeySection.EncryptOrDecryptData(false, buf, this.GetMacKeyModifier(), 0, length);
?length = buf.Length;
}
else if (EnableViewStateMac)
{
buf = MachineKeySection.GetDecodedData(buf, this.GetMacKeyModifier(), 0, length, ref length);
}
MemoryStream memoryStream = GetMemoryStream();
memoryStream.Write(buf, 0, length);
return this.Deserialize(memoryStream);
將字符串進行Base64解碼為字節流,如果需要解密則進行解密處理,否則需要進行需要Mac則進行Mac處理,將字節流轉換為內存流,進行反序列化返回Pair對象。
?
ViewState機制由淺入深2?
2.2.2?? Pair類及ViewState的存儲
Page及所有控件的ViewState、ControlState都是存儲在Pair類的實例中,了解Pair類及ViewState如何存儲在Pair類中很重要。Pair定義的System.Web.UI中具體定義如下:
public sealed class Pair
{
??? public object First;
??? public object Second;
??? public Pair();
??? public Pair(object x, object y);
}
可以看出Pair類是用作存儲兩個相關對象的基本結構。Pair是一個工具類,使用它很容易形成一個樹的數據結構。將__VIEWSTATE反序列化后就是一個Pair對象。這個對象即保存了控件之間的父子關系,也保存了ViewState信息。ViewState中的Key/Value對被轉換為一個ArrayList對象保存在Pair對象中。下面我們來看看它是如何存儲的。
圖3
圖3是對Pair的一種圖式表示方式,左邊表示First對象,右邊表示Second對象,根據這種圖式方式,圖4是利用Pair對象存儲ViewState的結構,對于ControlState沒有進行具體的展開。從savedState(Pair)開始采用了遞歸的存儲方式,將所有控件的ViewState及關系存儲起來,在Pair對象的First中存儲ViewState數據,在Second中存儲子控件的信息。
圖4
2.2.3?? Control中的處理
Page類繼承自Control類,在實現ViewState機制的時候Page類中主要是涉及序列化和反序列化的工作,ViewState的保存、裝載等功能都在Control類中實現由Page類來繼承,控件也都繼承自Control類,下面對Control類中和ViewState機制相關的屬性、方法進行介紹。1)???? ViewState屬性ViewState是Page及控件的一個屬性,這個屬性并不是在Page或控件中定義,它定義在System.Web.UI.Control類中。它的聲明如下:protected virtual StateBag ViewState{get}
由于所有服務器端的控件,用戶自定義控件,及Page類都是繼承自Control類,所以它們都會都具有一個protected的ViewState屬性。這個ViewState屬性在ViewState機制中很重要,它用來存儲控件需要記憶的一些數據。這些數據主要有兩類,一類是需要記憶的控件的屬性。另外一類是我們想利用ViewState機制來記住的一些數據,比如我們在Web窗體中寫如下的代碼:??? protected void Page_Load(object sender, EventArgs e)
??? {
??????? if (!this.IsPostBack)
??????? {
??????????? this.ViewState["Test"] = 0;
??????? }
??????? else
??????? {
??????????? this.ViewState["Test"]=int.Parse(this.ViewState["Test"].ToString()) + 1;
??? ????}
??? }因為我們創建的Web窗體都是繼承自Page類,所以在Web窗體中能否訪問Page類的protected的ViewState屬性。我們可以通過ViewState屬性利用ViewState機制,讓它幫助我們記住一些信息。ViewState屬性的主要作用還是用來記住一些控件的屬性值。ViewState屬性和控件的其他屬性有什么樣的聯系,才能夠利用ViewState屬性來記住他們呢?下面用Button類的Text屬性舉例來說明,Text屬性和ViewState屬性是什么關系。Button.Text的定義如下:public string Text
{
??? get
??? {
??????? string str = (string) this.ViewState["Text"];
??????? if (str != null)
??????? {
??????????? return str;
??????? }
??????? return string.Empty;
??? }
??? set
??? {
??????? this.ViewState["Text"] = value;
??? }
}
通過上面的代碼我們可以看出Button.Text屬性的值實際上和ViewState中Key為“Text”的Value是關聯的。當然Button類的其他屬性也是類似的方式,只是對應ViewState中Key不同而已。這樣只要ViewState的值能夠被記憶,那么Button類的屬性也就能夠被記憶住了。記憶ViewState的方法就是在Page. SaveAllState中將所有控件的ViewState屬性生成一個對象(這是一個特殊的對象,它是一個樹狀態的存儲了所有控件的ViewState屬性),然后將這個對象序列化為字符串,發送到客戶端。在下次請求的時候,將這個字符串發送給服務器,在Page.LoadAllState中將這個字符串反序列化為一個對象,將這個對象中存儲的各個控件的ViewState屬性,加載給各個控件的ViewState屬性。當我們訪問控件的屬性的時候就實現了對控件屬性的記憶,之前設置的控件屬性沒有丟失。2)???? LoadViewStateByID屬性LoadViewStateByID的聲明如下:
protected bool LoadViewStateByID{get}
LoadViewStateByID獲取一個值,該值指示控件是否通過ID方式加載其視圖狀態。默認情況下是通過索引方式加載視圖狀態,索引方式是依賴子控件在父控件的Controls集合中位置。ID方式會根據ID查找控件,效率比較低些,但是有些情形必須使用這種方式比如延遲創建子控件時。
3)???? EnableViewState屬性EnableViewState是一個bool類型的屬性,來決定當前控件的ViewState機制是否可用。其聲明如下:public virtual bool EnableViewState{get,set}
4)???? LoadViewStateRecursiveLoadViewStateRecursive的聲明如下:internalvoid LoadViewStateRecursive(object savedState)Page.LoadAllState中調用Control.LoadViewStateRecursive,傳入的參數savedState是一個Pair類型的對象,Pair.Fisrt中保存當前控件的ViewState,Pair.Second中保存子控件的ViewState。對于EnableViewState為true時,先通過調用Control.LoadViewState(savedState.First)裝載當前控件的ViewState。之后根據LoadViewStateByID屬性,裝載子控件的ViewState。如果LoadViewStateByID屬性為true調用Control.LoadChildViewStateByID(savedState.Second),否則調用Control.LoadChildViewStateByIndex(savedState.Second)。savedState.Second是一個ArrayList類型的對象。Control.LoadViewState的聲明如下:
protected virtual void LoadViewState(object savedState)在Control.LoadViewState中并沒有自己實現裝載當前控件的ViewState,而是通過ViewState.LoadViewState(savedState)來實現,下面會介紹StateBag的LoadViewState方法。LoadChildViewStateByID的聲明如下:internal void LoadChildViewStateByID(ArrayList childState)參數childState是ArrayList類型,childState中存儲了當前控件的所有子控件的信息。它的格式是首先是一個控件的ID,其后是這個控件的Pair對象。對childState進行循環,循環中取得控件的ID,根據ID找到控件調用這個控件的LoadViewStateRecursive方法。示意代碼如下:for (int i = 0; i < count; i += 2)
??? {
??????? string id = (string) childState[i];
??????? object savedState = childState[i + 1];
???????Control control = this.FindControl(id);
??????? control.LoadViewStateRecursive(savedState);
??? }
LoadChildViewStateByIndex的聲明如下:internal void LoadChildViewStateByIndex(ArrayList childState)
childState的存儲格式是首先是一個控件的索引,其后是這個控件的Pair對象。根據索引訪問訪問這個控件,調用其LoadViewStateRecursive方法,示例代碼如下:
??? for (int i = 0; i < num2; i += 2)
??? {
??????? int num4 = (int) childState[i];
??????? object savedState = childState[i + 1];
??????? controls[num4].LoadViewStateRecursive(savedState);
???? }
5)???? LoadViewState在LoadViewState中直接調用ViewState的LoadViewState方法進行ViewState的裝載,示意代碼如下:
this.ViewState.LoadViewState(savedState);
6)???? SaveViewStateRecursiveSaveViewStateRecursive的聲明如下:internal object SaveViewStateRecursive()
對于EnableViewState為true時,先調用Control.SaveViewState返回一個包含當前控件的ViewState信息的ArrayList類型對象x。之后對子控件進行遞歸處理獲得一個ArrayList類型的對象z。它的格式是ID(String),savedState(Pair)或者Index(int)savedState(Pair)。最后創建一個Pair對象Pair(x, z)。示意代碼如下:
object x = this.SaveViewState();
ArrayList z = null;
ControlCollection controls = this._occasionalFields.Controls;
int count = controls.Count;
bool loadViewStateByID = this.LoadViewStateByID;
for (int i = 0; i < count; i++)
{
????????? Control control = controls[i];
????????? object obj4 = control.SaveViewStateRecursive();
????????? if (loadViewStateByID)
??????????????????? z.Add(control.ID);
????????? else
????????? ????????? ????????? z.Add(i);
????????? z.Add(obj4);
}
return new Pair(x, z);
7)???? SaveViewState在SaveViewState中直接調用ViewState的SaveViewState方法進行ViewState的保存,示意代碼如下:
return this.ViewState.SaveViewState();
8)???? TrackViewState在Control.InitRecursive中會調用Control.TrackViewState,因為Control.InitRecursive是被遞歸調用的,所以每個控件的TrackViewState都會在初始化階段被調用到。Control.TrackViewState中之間調用ViewState的TrackViewState方法,示意代碼如下:
this.ViewState.TrackViewState();
ViewState機制由淺入深3?
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.1?ViewState的優缺點
使用ViewState首先要了解ViewState與其他的保持狀態機制相比有什么優缺點。
使用ViewState具有以下優點:
一、耗費的服務器資源較少(與Application、Session相比)。因為,視圖狀態數據都寫入了客戶端計算機中。
二、易于維護。默認情況下,DotNet系統自動啟用對狀態數據的維護。
三、因為它不使用服務器資源、不會超時,并且適用于任何瀏覽器。
使用視圖狀態具有以下缺點:
一、性能問題。由于視圖狀態存儲在頁本身,因此如果存儲較大的值,用戶顯示頁和發送頁時的速度仍然可能減慢。ViewState 增加了發送到瀏覽器的頁面的大小,同時也增加了回傳的窗體的大小,因此不適合存儲大量數據
二、設備限制。移動設備可能沒有足夠的內存容量來存儲大量的視圖狀態數據。
三、潛在的安全風險。視圖狀態存儲在頁上的一個或多個隱藏域中。雖然視圖狀態以哈希格式存儲數據,但它可以被篡改。如果直接查看頁輸出源,可以看到隱藏域中的信息,盡管 ViewState 數據已被編碼,并且可以選擇對其進行加密,但始終不將數據發送到客戶端才是最安全的。
4.2?ViewState安全性
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.3?ViewState的禁用
因為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)控件沒有動態的或數據綁定的屬性值。
轉載于:https://www.cnblogs.com/PeterYao/archive/2009/10/13/1582829.html
總結
以上是生活随笔為你收集整理的ViewState机制的解析(转自csdn)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 改变背景色为苹果绿--保护眼睛
- 下一篇: 类关系图