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