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對象。