为Web页面及其控件保持状态信息是非常有必要的然而由于Web应用程序创建于HTTP协议的顶层这是一个无状态的协议因此保持状态信息则变得非常困难为了解决这个问题 技术提供了多种解决方案例如利用SessionCookie视图状态控件状态隐藏域查询字符串个性化用户配置(Profile)等等对于利用ASPNET 技术创建服务器控件而言保持状态信息也是非常重要的其主要解决途径是利用视图状态和控件状态本文详细讲解了视图状态(ViewState)的基本知识并通过典型应用介绍视图状态的应用方法
视图状态概述
视图状态是一项非常重要的技术它能使得页面和页面中的控件在从服务器到客户端再从客户端返回的往返过程中保持状态信息这样就可以在Web这种无状态的环境之上创建一个有状态并持续执行的页面效果本节主要介绍有关视图状态的运行机制应用方法存储的数据类型性能和安全性视图状态分块(这是 ASPNET 的新特性)和优缺点等内容
()运行机制
视图状态的具体运行过程为每当用户请求某个aspx页面时NET框架首先把相关控件的状态数据序列化成一个字符串然后将其做为名为__VIEWSTATE的隐藏域的Value值发送到客户端如果页面是第一次被请求那么服务器控件也将是被第一次执行时名为__VIEWSTATE的隐藏域中只包含控件的默认信息通常为空或者 null在随后的回送事件中ViewState中就保存了服务器控件在前面回送中可用的属性状态这样服务器控件就可以监视在当前被处理的回送事件发生之前的状态了这些过程是由NET框架负责的对用户来说是执行aspx页面就有了持续执行的效果
()存储的数据类型
视图状态可以存储多种类型的数据并且为了提高运行效率视图状态自身还包括一套已经优化的针对常用类型的序列化方式视图状态序列化方式默认支持的数据类型包括以下几种StringIntUnitColorArrayArrayListHashTable和自定义类型转换器 TypeConverter
视图状态已经为ArrayArrayList和包含上面列出类型的HashTable对象进行了优化因此当在控件中使用视图状态时应该试着限定于使用以上简单数据类型以及经过优化的类型在此需要重点说明一下自定义类型转换器 TypeConverter它提供了一种将值的类型转换为其他类型以及访问标准值和子属性的统一方法例如可以利用TypeConverter将字符串转换为数值或者将数值转换为字符串如果没有类型转换器那么页面框架会使用NET框架提供的二进制序列化功能来序列化对象这个过程是非常耗费资源的
()性能和安全性
使用视图状态时对象必须先序列化然后再通过回传进行反序列化因此我们必须了解有关ViewState性能的内容默认情况下控件的ViewState将被启用如果不需要使用ViewState最好还是将它关闭以下情况将不再需要ViewState()控件未定义服务器端事件(这时的控件事件均为客户端事件且不参加回送的)()控件没有动态的或数据绑定的属性值关闭视图状态的方法是将控件的EnableViewState的值设置为false即EnableViewState=false
默认情况下视图状态的有关内容在编译运行发送给客户端时读者将在页面的HTML代码中看到__VIEWSTATE隐藏域内容这是一些没有意义的字符串是NET框架通过Base位编码对相关内容编码的结果它们是通过明文方式在客户端和服务器端之间往返传送在某些情况下例如涉及密码账号连接字符串等敏感内容时使用默认方式是很不安全的为此NET框架为ViewState提供了两种安全机制
· 校验机制
可以通过设置EnableViewStateMAC=true属性来指示NET框架向ViewState数据中追加一个散列码(该散列码是一种 SHA类型长度有位因此会严重影响执行性能)在回传事件发生时将重新建立该散列码它必须和最初的散列码匹配通过这种方式能够有效检验ViewState是否在传送过程中能够被篡改默认情况下NET框架使用SHA算法来生成ViewState散列代码此外也可以通过在 nfig文件中设置<machineKey>来选择 MD 算法如下所示<machineKey validation=MD />MD算法的性能要比SHA算法好一些但是同样不够安全
· 加密机制
使用加密来保护ViewState字段中的实际数据值首先必须如上所述设置EnableViewStatMAC=true然后将 machineKey validation类型设置为DES即<machineKey validationKey=AutoGenerate decryptionKey=AutoGenerate validation=DES />这指示ASPNET使用DES加密算法来加密ViewState值
()视图状态分块
以上内容介绍了视图状态的一些基本知识然而可能部分读者会有些疑惑如果在某些情况下视图状态数据变得很大那怎么办呢?这样显然会出现一些意想不到的后果为此ASPNET 新增了一种名为视图状态分块的功能如果视图状态的数据量变得太大视图状态分块自动将数据分成多个块区并将这些数据放在多个隐藏形式的字段中
若要启用视图状态分块可将MaxPageStateFieldLength属性设置为在单个视图状态字段中允许的最大大小(以字节为单位)当该页回发到服务器时该页会在页初始化阶段分析视图状态字符串并还原页中的属性信息默认设置是这表示不存在最大大小不会将视图状态分成多个块区
()优点和缺点
使用视图状态具有以下个优点一耗费的服务器资源较少(与 ApplicationSession相比)因为视图状态数据都写入了客户端计算机中二易于维护默认情况下NET系统自动启用对控件状态数据的维护三增强的安全功能视图状态中的值经过哈希计算和压缩并且针对Unicode实现进行编码其安全性要高于使用隐藏域
使用视图状态具有以下个缺点一性能注意事项由于视图状态存储在页本身因此如果存储较大的值即使在视图状态分块的情况下用户显示页和发送页时的速度仍然可能减慢二设备限制移动设备可能没有足够的内存容量来存储大量的视图状态数据因此移动设备上的服务器控件时将使用其他的实现方法三潜在的安全风险视图状态存储在页上的一个或多个隐藏域中虽然视图状态以哈希格式存储数据但它可以被篡改如果直接查看页输出源可以看到隐藏域中的信息这导致潜在的安全性问题
在利用 技术进行服务器控件开发过程中有很多方面可以用到视图状态常见的是利用ViewState字典实现服务器控件属性ViewState是SystemWebUIStateBag类型一个键/值对的字典服务器控件的属性值可以存储在ViewState中下面通过一个典型示例说明ViewState的应用方法
在自定义服务器控件LabelInViewState中实现了两个属性Text和TextInViewState前者使用私有变量创建后者使用 ViewState实现它们都用于获取或者设置文本内容自定义控件实现文件LabelInViewStatecs源代码如下所示
using System;using SystemCollectionsGeneric;
using SystemComponentModel;using SystemText;
using SystemWeb;
using SystemWebUI;
using SystemWebUIWebControls;namespace WebControlLibrary{
[DefaultProperty(Text)]
[ToolboxData(<{}:LabelInViewState runat=server></{}:LabelInViewState>)]
public class LabelInViewState : WebControl {
private string _text; //实现Text属性
public string Text {
get {
return (_text == null) ? stringEmpty : _text;
}
set { _text = value; }
}
//使用ViewState实现TextInViewState属性
public string TextInViewState {
get {
String s = (String)ViewState[TextInViewState];
return ((s == null) ? StringEmpty : s);
}
set { ViewState[TextInViewState] = value; }
}
// 重写RenderContents方法
protected override void RenderContents(HtmlTextWriter output) {
outputWrite(Text = );
outputWrite(Text);
outputWrite(<br/>);
outputWrite(TextInViewState = );
outputWrite(TextInViewState);
}
}
}
如上代码所示控件实现了两个属性Text和TextInViewStateText属性使用了私有变量_text创建这种实现无法保持该属性的状态信息TextInViewState属性使用了ViewState其通过set访问器将属性值写入ViewState [TextInViewState]对象中通过get访问器从对象ViewState[TextInViewState ]中获取属性值这就是视图状态处理最简单的方法当使用ViewState作为属性存储时自定义服务器控件可以自行完成简单的状态信息管理例如 TrackViewStateSaveViewStateLoadViewState等当然开发人员也可以通过重写方法自定义状态管理逻辑程序在本例中视图状态管理过程都是由NET框架自动完成的
下面列举了为测试以上自定义服务器控件而创建的Defaultaspx文件源代码
<%@ Page Language=C# AutoEventWireup=true CodeFile=Defaultaspxcs Inherits=_Default %>
<%@ ReGISter Namespace=WebControlLibrary Assembly=WebControlLibrary TagPrefix=sample %>
<!DOCTYPE html PUBLIC //WC//DTD XHTML Transitional//EN transitionaldtd>
<script runat=server>
void Button_Click(object sender EventArgs e) {
demoLabelText = TextBoxText;
demoLabelTextInViewState = TextBoxText;
}
</script>
<html XMLns=>
<head runat=server>
<title>使用视图状态ViewState</title>
</head>
<body >
<form id=form runat=server> <div> 姓名XML:namespace prefix = asp />
以上代码显示在页面中包括两个文本框两个按钮以及一个自定义服务器控件LabelInViewState如事件处理程序 Button_Click所示当单击提交按钮时LabelInViewState控件将获取文本框中文本并显示出来应用程序效果图如图和图所示
如图所示当用户在两个文本框中填写了文本并单击提交按钮引发页面回传此时填写的文本内容将提交到服务器并参与 Button_Click事件处理程序这样LabelInViewState控件则显示出了Text和TextInViewState属性值之后当用户单击重载按钮时文本框内容仍然提交到服务器但是由于没有对应的事件处理程序因此LabelInViewState控件只显示已经存在的状态信息(即单击提交按钮之后保存的状态)即Text属性值为空而TextInViewState属性值为通过以上过程可知TextInViewState属性值都存储在视图状态ViewState中因此在页面往返过程中该属性值得以保持而Text只简单使用了私有变量所以状态信息无法保持另外需要注意的是由于默认情况下页面启用了视图状态EnableViewState = true才能实现以上效果
小结
本文主要介绍了视图状态的基本概念并通过一个典型示例说明了应用方法可能部分读者已经认识到如果禁用了页面或者控件的视图状态即设置EnableViewState = false那么上文服务器控件的属性TextViewState不是不能使用了吗?这的确是视图状态的缺陷所在然而这并不是说就无法解决这个问题了在下文中笔者将介绍另外一种ASPNET 新增的与视图状态极为类似的技术特性控件状态它就能够很好的解决禁用视图状态的问题