ViewState机制由浅入深1
1 ViewState机制是什么? ViewState机制是asp.net中对同一个Page的多次请求(PostBack)之间维持Page及控件状态的一种机制。在WebForm中每次请求完,Page对象都会被释放,对同一个Page的多次请求之间的状态信息,如何进行维护呢?WebForm中,每次请求都会存在客户端和服务器之间的一个交互。如果请求完成之后将一些信息传回到客户端,下次请求的时候客户端再将这些状态信息提交给服务器,服务器端对这些信息使用和处理,再将这些信息传回给客户端。这样是不是就可以对同一个Page的多次请求(PostBack)之间维持状态了。对这就是ViewState的基本工作模式。ViewState的设计目的主要就是为了将必要的信息持久化在页面中。这样通过ViewState在页面回传的过程中保存状态值,使原本没有“记忆”的Http协议变得有“记忆”起来。 2 ViewState机制如何工作? 下面我们看看ViewState机制是如何具体的工作的。 2.1 客户端: 我们先从客户端看起,在客户端的HTML源代码中我们可以看到下面的代码 这个就是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对象。
转载:http://www.cnblogs.com/hobe/archive/2008/03/25/1122203.html