现在的位置: 首页 > 综合 > 正文

对viewstates的理解更深入了(2)

2013年10月11日 ⁄ 综合 ⁄ 共 2966字 ⁄ 字号 评论关闭

关于ViewState不得不说的(原创,转载请声明)

一直以为ViewState是把页面所有的值存储起来,而且好多的Asp.net书籍也是这么说的,或者是我比较愚笨,没有弄明白,直到最近在做动态生成控件,在更深入的了解了一下ViewState。

在我进行讲解之前,先说一下我用到的两个工具:一个是ViewStateDecoder(winform的),以前有一个webform的叫ViewState Parser,可惜Paul Wilson不提供连接了,反正我是没找到;另一个是TcpTrace,用来查看http协议的客户端请求和服务器回应(有一个类似的程序ieHttpHeaders,不过好像不能查看服务器回应)

还有一个经常用到的函数ServerUtility.Decode(string s),因为TcpTrace把客户端的请求进行了encode,所以我们在把他放到ViewStateDecoder之前,应该先decode一下。

在开始讲解之前,我想首先把http协议的客户端请求和服务器回应简单的讲解一下。简单说,客户端发出一个请求,服务器根据这个请求返回一个回应,下面我们通过TcpTrace来看一下请求和回应的内容。

我们来看一个简单的html页面

<body>
<form action=http://localhost:8080/ method="post">
<input type="text" name="txt" value="test">
<input type="submit" value="smit">
</form>
</body>

在运行这个页面之前,我们首先需要将TcpTrace打开进行监听,TcpTrace按照 默认的设置(listen port:8080; destination port:80;server:localhost)就可以。这里我们解释一下form的action设置:设置为localhost:8080说明我们监听了我们本机的8080端口(安装了IIS、Apache、Tomcat等就可以把本机当做服务器),由于我们这里只想看客户端的请求,所以action的具体文件可以不指定(1)。

但我们点击页面提交(请确保此时TcpTrace已经开始监听),会在TcpTrace中看到请求的内容 (2)

我们看到请求的内容包括form表单内容的键值对。

我们再看一下aspx页面的情况,在番茄鸡蛋面Some Articles Help You To Understand ASP.NET ViewState 中提到

ViewState不是用来恢复Form(此处Form专门指普通的,用来向服务器端提交数据的表单)的值(value)的.
比如Input 里面的TextBox,Submit, CheckBox, Select等等,这些control由于实施了IPostBackDataHandler接口,所以他们的value并没有保存在ViewState里面,因此也不用ViewState来恢复.

我在这里以例子的形式在说明一下,举一个最简单的例子,注意这里文件的后缀名是aspx

<form id="Form1" method="post" runat="server">
<asp:TextBox id="TextBox1" runat="server"></asp:TextBox>
<asp:Button id="Button1" runat="server" Text="Button"></asp:Button>
</form>

大家应该知道asp.net页面的一个重要机制就是自提交,因此我们考察页面自提交后的客户端请求内容和服务器的回应内容

我们看一下自提交以后客户端请求的post内容包括两个名-值对 __ViewState和TextBox1。用我们前面提到的函数urlDecode对post内容解码或者直接用服务器回应的内容中隐藏域__ViewState的内容,通过ViewStateDecoder我们可以看到

textbox的内容包含在ViewState中,这样看好像和番茄鸡蛋面提到的有些矛盾,那我们再来看一下如果把Textbox的EnableViewState设置成false,运行后结果

发现textbox的内容不再保存在ViewState中,但是我们可以看到在自提交以后textbox的值仍然呈现在页面中,可见,textbox的值确实不受ViewState的控制。实际上是因为textbox继承了IPostBackDataHandler接口,通过Reflector,我们看一下TextBox的源代码

bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection)
{
string text1 = this.Text;
string text2 = postCollection[postDataKey];
if (!text1.Equals(text2))
{
this.Text = text2;
return true;
}
return false;
}

其中名值对是把页面中所有标记为runat="server"的标签的Name(注意是Name不是ID)和Value值保存起来,也就是我们前面提到的自提交客户端post的除了__ViewState的内容,在LoadPostData中我们通过postDataKey也就是标签的Name查找到Value进行填值。

你也许会奇怪为什么这里会要求Name而不是ID,其实这是和http协议post方法有关的,记得我们最前面讨论的html页面,必须用name而不是id。看一下testbox的AddAttributesToRender方法

writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID);

可见,这里给name进行了赋值。因此我们发现此处ViewState实际上是不需要的。

结论:

理解了这点,我们可以更大程度的缩减ViewState大小;借助于ViewStateDecoder和TcpTrace,我们可以观察ViewState的变化,对于开发动态控件的问题很有帮助。

注:

(1)如果method为post,action为静态的文件如htm,会报如下错误:

405错误,资源被禁止,具体的服务器回应为

HTTP/1.1 405 Method not allowed

Server: Microsoft-IIS/5.1

Date: Tue, 20 Sep 2005 09:18:31 GMT

Connection: close

Allow: OPTIONS, TRACE, GET, HEAD, PUT, DELETE

这种情况应该和服务器的设置有关。

(2)如果我们只想判断该页面是否存在,而不是把所有的服务器回应全部得到,在jscript中可以这样写

XMLHTTP = new ActiveXObject("Microsoft.XMLHTTP")
XMLHTTP.open("HEAD",url,false)
XMLHTTP.send()

这样写可以介绍网络的流量,提高速度。对于在其他语言中的写法,由于时间的关系,这里不再提供,请读者自行查找。

抱歉!评论已关闭.