ViewState 剖析(翻译兼笔记)

发表于:2007-06-30来源:作者:点击数: 标签:
ViewState 不是什么? 1. ViewState 不是用来恢复回发的控件的值。 这个是通过匹配 form 中该控件的变量名而自动完成的。这个只对 Load 事件加载之前创建的控件有效。 2. ViewState 不会自动重新创建任何通过代码动态创建的控件。 3. 不是用来保存用户信息的
ViewState 不是什么?

1. ViewState 不是用来恢复回发的控件的值。
这个是通过匹配 form 中该控件的变量名而自动完成的。这个只对 Load 事件加载之前创建的控件有效。
2. ViewState 不会自动重新创建任何通过代码动态创建的控件。
3. 不是用来保存用户信息的。仅仅保存本页的控件状态,而不能在页面之间传递。

ViewState 是什么?

ViewState 用来跟踪和保存控件的状态信息。否则这些信息可能会丢失,原因可能是这些值不随着 form 回发,或者根本就不在 page 的 html 中。
ViewState 中保存着代码中改变的控件属性,通过代码绑定到控件的任何数据,以及由用户操作触发,回发的任何更改。
ViewState 还提供了一个状态包(StateBag), 这是一个特殊的集合或字典(collection or dictionary), 可以用来保存,通过一个 key 来恢复任意的对象或者值。

ViewState 的格式

保存在表单中的 __VIEWSTATE 隐藏字段。是 Base64 编码过的,而不是加密!
但要加密也是可以的(设置 enableViewStateMac 来使用 machine key 进行 hash)
加密:设置 machineKey 验证, 但这必须在机器级别设置,需要更多的资源,所以不推荐。

Listing 1: ViewState Machine Hash Disabled
machine.config or web.config: <pages enableViewStateMac=‘’false‘’ />
page level directive: <%@Page enableViewStateMac=‘’false‘’ %>
page level script code: Page.EnableViewStateMac = false;

Listing 2: ViewState Encryption is Enabled
machine.config: <machineKey validation=‘’3DES‘’ validationKey=‘’*‘’ />
where the validationKey must be the same across a web-farm setup
also requires the enableViewStateMac property setting to be true

在 rendering 之前,ViewState 在 Page.SavePageStateToPersistenceMedium 方法中被保存,
回发时,在 Page.LoadPageStateFromPersistanceMedium 方法中被恢复。
这两个方法都可以轻易的被重写,从而实现保存 ViewState 到 Session 中。这适合于带宽小的场合,
如移动设备默认是采用 Session.代码如下:

Listing 3: ViewState Saved in Session State
protected override object LoadPageStateFromPersistenceMedium()
{
return Session["ViewState"];
}
protected override void SavePageStateToPersistenceMedium(object viewState)
{
Session["ViewState"] = viewState;
// Bug requires Hidden Form Field __VIEWSTATE
RegisterHiddenField("__VIEWSTATE", "");
}
如果要把 ViewState 通过数据库或其他持久化设备来维持,则需要采用特定的 LosFormatter 类来序列化,反序列化。(serialize, deserialize)
Listing 4: ViewState Saved in Custom Store
protected override object LoadPageStateFromPersistenceMedium()
{
LosFormatter format = new LosFormatter();
return format.Deserialize(YourDataStore["ViewState"]);
}
protected override void SavePageStateToPersistenceMedium(object viewState)
{
LosFormatter format = new LosFormatter();
StringWriter writer = new StringWriter();
format.Serialize(writer, viewState);
YourDataStore["ViewState"] = writer.ToString();
}

最后,我们来看一下 ViewState 的内部格式到底是什么。
每个控件的 ViewState 保存在一个三元组中(Triplet, System.Web.UI.Triplet).
其 First 对象是:
一个 Pair(System.Web.UI.Pair)

Array or Pairs, of ArrayLists of related name-values.
Second 对象:
该控件在控件树中的索引的 ArrayList
Third 对象:
子控件的类似的三元组的 ArrayList
Listing 5: ViewState Decode/Parse Example
编码后的 ViewState:
dDwxMjM0NTY3ODkwO3Q8cDxsPHBycEE7cHJwQjtwcnBDOz47bDx2YWxBO3ZhbEI7dmFsQzs+PjtsPGk8
MD47aTwyPjtpPDM+O2k8NT47PjtsPHQ8cDxsPHBycEE7cHJwQjs+O2w8dmFsQTt2YWxCOz4+Ozs+O3Q8
cDxsPHBycEE7cHJwQjs+O2w8dmFsQTt2YWxCOz4+Ozs+O3Q8cDxsPHBycEE7cHJwQjs+O2w8dmFsQTt2
YWxCOz4+Ozs+O3Q8cDxsPHBycEE7cHJwQjs+O2w8dmFsQTt2YWxCOz4+Ozs+Oz4+Oz4=

解码后的 ViewState:
t<1234567890;t<p<l<prpA;prpB;prpC;>;l<valA;valB;valC;>>;
l<i<0>;i<2>;i<3>;i<5>;>;l<
t<p<l<prpA;prpB;>;l<valA;valB;>>;;>;
t<p<l<prpA;prpB;>;l<valA;valB;>>;;>;
t<p<l<prpA;prpB;>;l<valA;valB;>>;;>;
t<p<l<prpA;prpB;>;l<valA;valB;>>;;>;>>;>

解析后的 ViewState:
t<1234567890; 页面级别的三元组是特例
t<p<l<prpA;prpB;prpC;>; Triplet-First:Pair-First:ArrayList
l<valA;valB;valC;> Pair-Second:ArrayList
>;
l<i<0>; Triplet-Second:ArrayList:Indices
i<2>; of the
i<3>; Children
i<5>; Controls
>;
l<t<p<l<prpA;prpB;>; Triplet-Third:ArrayList:Triplets
l<valA;valB;> of the
>; Children
; Controls
>;
t<p<l<prpA;prpB;>; Each Sub-Triplet follows same Pattern
l<valA;valB;>
>;
; More Levels Possible if sub-Children
>;
t<p<l<prpA;prpB;>; Each Sub-Triplet follows same Pattern
l<valA;valB;>
>;
; More Levels Possible if sub-Children
>;
t<p<l<prpA;prpB;>; Each Sub-Triplet follows same Pattern
l<valA;valB;>
>;
; More Levels Possible if sub-Children
>;
>
>; Closing of Special Page-Level Triplet
>

Listing 6: ViewState Decode/Parse Code
1using System;
2using System.Collections;
3using System.ComponentModel;
4using System.Data;
5using System.Drawing;
6using System.Web;
7using System.Web.SessionState;
8using System.Web.UI;
9using System.Web.UI.WebControls;
10using System.Web.UI.HtmlControls;
11using System.IO;
12using System.Text;
13
14namespace MyPlayground
15{
16 /// <summary>
17 /// ShowViewState 的摘要说明。
18 /// </summary>
19 public class ShowViewState : System.Web.UI.Page
20 {
21 private void Page_Load(object sender, System.EventArgs e)
22 {
23 //Trace.Warn("分类名称", "^_^,这是警告!自动用红色字显示");
24 //Trace.Write("这是普通的消息写入!");
25 }
26 #region Web 窗体设计器生成的代码 ...
27
28 override protected void OnInit(EventArgs e)
29 {
30 //
31 // CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。
32 //
33 InitializeComponent();
34 base.OnInit(e);
35 }
36
37 /// <summary>
38 /// 设计器支持所需的方法 - 不要使用代码编辑器修改
39 /// 此方法的内容。
40 /// </summary>
41 private void InitializeComponent()
42 {
43 this.Load += new System.EventHandler(this.Page_Load);
44
45 }
46 #endregion
47
48 protected override void SavePageStateToPersistenceMedium(object viewState)
49 {
50 // 调用基类的方法以便不影响正常的处理
51 base.SavePageStateToPersistenceMedium(viewState);
52 // 读取 ViewState 并写到页面
53 LosFormatter format = new LosFormatter();
54 StringWriter writer = new StringWriter();
55 format.Serialize(writer, viewState);
56 string vsRaw = writer.ToString();
57 Response.Write("ViewState Raw: " + Server.HtmlEncode(vsRaw) + "<hr>");
58 // 解码 ViewState 并写到页面
59 byte[] buffer = Convert.FromBase64String(vsRaw);
60 string vsText = Encoding.ASCII.GetString(buffer);
61 Response.Write("ViewState Text: " + Server.HtmlEncode(vsText) + "<hr>");
62 // 解析 ViewState -- 打开页面跟踪(Tracing)
63 ParseViewState(viewState, 0);
64 }
65 private void ParseViewState(object vs, int level)
66 {
67 if (vs == null)
68 {
69 Trace.Warn(level.ToString(), Spaces(level) + "null");
70 }
71 else if (vs.GetType() == typeof(System.Web.UI.Triplet))
72 {
73 Trace.Warn(level.ToString(), Spaces(level) + "Triplet");
74 ParseViewState((Triplet) vs, level);
75 }
76 else if (vs.GetType() == typeof(System.Web.UI.Pair))
77 {
78 Trace.Warn(level.ToString(), Spaces(level) + "Pair");
79 ParseViewState((Pair) vs, level);
80 }
81 else if (vs.GetType() == typeof(System.Collections.ArrayList))
82 {
83 Trace.Warn(level.ToString(), Spaces(level) + "ArrayList");
84 ParseViewState((IEnumerable) vs, level);
85 }
86 else if (vs.GetType().IsArray)
87 {
88 Trace.Warn(level.ToString(), Spaces(level) + "Array");
89 ParseViewState((IEnumerable) vs, level);
90 }
91 else if (vs.GetType() == typeof(System.String))
92 {
93 Trace.Warn(level.ToString(), Spaces(level) + "‘’" + vs.ToString() + "‘’");
94 }
95 else if (vs.GetType().IsPrimitive)
96 {
97 Trace.Warn(level.ToString(), Spaces(level) + vs.ToString());
98 }
99 else
100 {
101 Trace.Warn(level.ToString(), Spaces(level) + vs.GetType().ToString());
102 }
103 }
104 private void ParseViewState(Triplet vs, int level)
105 {
106 ParseViewState(vs.First, level + 1);
107 ParseViewState(vs.Second, level + 1);
108 ParseViewState(vs.Third, level + 1);
109 }
110 private void ParseViewState(Pair vs, int level)
111 {
112 ParseViewState(vs.First, level + 1);
113 ParseViewState(vs.Second, level + 1);
114 }
115 private void ParseViewState(IEnumerable vs, int level)
116 {
117 foreach (object item in vs)
118 {
119 ParseViewState(item, level + 1);
120 }
121 }
122
123 // 得到指定数目的空白
124 private string Spaces(int count)
125 {
126 string spaces = "";
127 for (int index = 0; index < count; index++)
128 {
129 spaces += " ";
130 }
131 return spaces;
132 }
133 }
134}
135
译注:上面代码由本人测试后加上了 VS.NET 自动生成的其他部分代码,为方便大家试验。

原文转自:http://www.ltesting.net