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

posted on 2008-03-25 23:20  启明  阅读(3402)  评论(5编辑  收藏  举报