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

ViewState機制由淺入深1

編輯:關於.NET

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

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