ASP.NET ViewState使用实例介绍
摘自:http://www.delphibbs.com/keylife/iblog_show.asp?xid=31099
1. ViewState的工作原理
ViewState是由ASP.NET页面框架管理的一个隐藏的窗体字段。当ASP.NET执行某个页面时,该页面上的ViewState值和所有控件将被收集并给和格式化成一个编码字符串,然后分配给隐藏窗体字段的值属性(即<input type=”hidden”>)。由于隐藏窗体字段是发送到客户端的页面的一部分,所以ViewState值被临时存储在客户端的浏览器中。如果客户端选择该页面回传给服务器,则ViewState字符串也将被回传。回传后,ASP.NET页面框架将解析ViewState字符串,并为该页面和各个控件填充ViewState属性。然后,控件再使用ViewState数据将自己重新恢复为以前的状态。
2. 关于ViewState要注意的问题
1、如果要使用ViewState,则在ASPX页面中必须有一个服务器端窗体标记(<form runat="server">)。窗体字段是必需的,这样包含ViewState信息的隐藏字段才能回传给服务 器。而且,该窗体还必须是服务器端的窗体,这样在服务器上执行该页面时,ASP.NET页面 框架才能添加隐藏的字段。
2、页面本身将20字节左右的信息保存在ViewState中,用于在回传时将PostBack数据和ViewState值分发给正确的控件。因此,即使该页面或应用程序禁用了ViewState,仍可以在ViewState中看到少量的剩余字节。
3、在页面不会传得情况下,可以通过省略服务器端的<form>标记来去掉页面中的ViewState。
3. 充分利用ViewState
ViewState为跨回传跟踪控件的状态提供了一条神奇的路径,因为它不使用服务器资源、不会超时,并且适用任何浏览器。
ViewState的创建和使用:
//保存在ViewState中
ViewState["SortOrder"] = "DESC";
//从ViewState中读取
string sortOrder = (string)ViewState["SortOrder"];
代码如下:
Default3.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default3.aspx.cs" Inherits="Default3" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>无标题页</title>
</head>
<body>
<form id="form1" runat="server">
<h3>
在 ViewState 中存储非控件状态
</h3>
<p>
此示例将一列静态数据的当前排序顺序存储在 ViewState 中。<br />
单击列标题中的链接,可按该字段排序数据。<br />
再次单击该链接,将按相反顺序排序。</p>
<p>
<asp:GridView ID="GridView1" runat="server" BackColor="White" BorderColor="#3366CC"
BorderStyle="None" BorderWidth="1px" CellPadding="4" AllowSorting="True" OnSorting="GridView1_Sorting">
<FooterStyle BackColor="#99CCCC" ForeColor="#003399" />
<RowStyle BackColor="White" ForeColor="#003399" />
<SelectedRowStyle BackColor="#009999" Font-Bold="True" ForeColor="#CCFF99" />
软件开发网 www.mscto.com
<PagerStyle BackColor="#99CCCC" ForeColor="#003399" HorizontalAlign="Left" />
<HeaderStyle BackColor="#003399" Font-Bold="True" ForeColor="#CCCCFF" />
</asp:GridView>
</p>
</form>
</body>
</html>
Default3.aspx.cs
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls; 软件开发网 www.mscto.com
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class Default3 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
BindGrid();
}
}
string SortField
{
get
{
object o = ViewState["SortField"];
if (o == null)
{
return String.Empty;
}
return (string)o;
}
set
{
if (value == SortField)
{
// 与当前排序文件相同,切换排序方向
SortAscending = !SortAscending;
}
ViewState["SortField"] = value;
}
}
// 在 ViewState 中跟踪 SortAscending 属性
bool SortAscending
{
get
{
object o = ViewState["SortAscending"];
if (o == null)
{
return true;
}
return (bool)o;
}
set
{
ViewState["SortAscending"] = value;
}
}
void BindGrid()
{
// 获取数据
DataSet ds = new DataSet();
ds.ReadXml(Server.MapPath("TestData.xml"));
DataView dv = new DataView(ds.Tables[0]);
// 应用排序过滤器和方向
dv.Sort = SortField;
if (!SortAscending)
{
dv.Sort += " DESC";
}
// 绑定网格
this.GridView1.DataSource = dv;
this.GridView1.DataBind();
}
protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{
this.GridView1.PageIndex = 0;
SortField = e.SortExpression;
BindGrid();
}
}
Testdata.xml
<?xml version="1.0" encoding="utf-8" ?>
<NewDataSet>
<Table>
<pub_id>0736</pub_id>
<pub_name>New Moon Books</pub_name>
<city>Boston</city>
<state>MA</state>
<country>USA</country>
</Table>
<Table>
<pub_id>0877</pub_id>
<pub_name>Binnet & Hardley</pub_name>
<city>Washington</city>
<state>DC</state>
<country>USA</country>
</Table>
<Table>
<pub_id>1389</pub_id>
<pub_name>Algodata Infosystems</pub_name>
<city>Berkeley</city>
<state>CA</state>
<country>USA</country>
</Table>
<Table>
<pub_id>1622</pub_id>
<pub_name>Five Lakes Publishing</pub_name>
<city>Chicago</city>
<state>IL</state>
<country>USA</country>
</Table>
<Table>
<pub_id>1756</pub_id>
<pub_name>Ramona Publishers</pub_name>
<city>Dallas</city>
<state>TX</state>
<country>USA</country>
</Table>
<Table>
<pub_id>9901</pub_id>
<pub_name>GGG&G</pub_name>
<city>Muenchen</city>
<country>Germany</country>
</Table>
<Table>
<pub_id>9952</pub_id>
<pub_name>Scootney Books</pub_name>
<city>New York</city>
<state>NY</state>
<country>USA</country>
</Table>
<Table>
<pub_id>9999</pub_id> 软件开发网 www.mscto.com
<pub_name>Lucerne Publishing</pub_name>
<city>Paris</city>
<country>France</country>
</Table>
</NewDataSet>
4. 选择会话状态还是ViewState?
在某些情况下,状态值保存在ViewState中部是最佳选择,最常用的替代方法就是会话状态,它通常更适用于:
1、大量的数据:由于 ViewState 增加了发送到浏览器的页面的大小(HTML 有效负载), 同时也增加了回传的窗体的大小,因此不适合存储大量数据。
2、未在UI中显示的安全数据:尽管ViewState数据已被编码,并且可以选择对其进行加密,但始终不将数据发送到客户端才是最安全的,因此,会话是更安全的选择。(由于数据库学要额外的凭据进行验证,因此将数据存储在数据库中会更安全。可以添加SSL以获得更安全的链接。)但是,如果在UI中已经显示了该专用数据,那么您应该已经确认了链接的安全性。在这种情况下,将同样的值放入ViewState不会降低安全性。
3、尚未序列化到ViewState中的对象,如DataSet。ViewState序列化程序只为一小部分常用的对象类型进行了优化,其他可序列化的类型或许可以保留在ViewState中,但速度会变慢,并会生成一个非常大的ViewState。
比较项
会话状态
ViewState
是否适用服务器资源
是
否
是否超时
是,20分钟后(默认)
否
是否存储所有.NET类型
是
否,仅支持:sting、int、bool、Array、ArrayList、Hashtable和自定义TypeConverter
是否增加“HTML有效负担”
否
是
5. 使用ViewState获得最佳性能
使用ViewState时,每个对象都必须先序列化到ViewState中,然后再通过回传进行反序列化,因此使用ViewState并非是没有代价的。但是,如果遵循某些简单的原则对ViewState的成本加以控制,并通常不会产生明显的性能影响。
1、 在不需要时禁用ViewState。
2、 使用优化过的ViewState序列化程序,上面列出的类型具有专门的序列化程序,这些程序运行速度很快,并已经过优化,可以生成很小的ViewState。如果要序列化一个未在上面列出的类型,可以创建一个自定义TypeConverter来显著提高它的性能。
3、 尽量减少使用对象,如果可能,尽量减少放入ViewState中的对象的数目。例如,不要使用尔维数组(名称/值,其对象的数目与数组的长度一样多),而应适用二个字符串数组(只有两个对象)。但是,在将二个已知类型存储在ViewState中之前,在这两者之间转换不会获得不会获得任何性能提高,因为这样做实际上相当于付出了两次转换的代价。
6. 减少使用ViewState
默认情况下,ViewState将被启用,并且是由每个控件(而非页面开发人员)来决定存储在viewState中的内容。有时,这一信息对应程序并没有什么用处。尽管也没什么害处,但却会明显增加发送到浏览器的页面的大小。因此如果不需要使用viewState,最好还是将它关闭,特别是当ViewState很大的时候。
可以基于每个控件、每个页面或每个应用程序来关闭ViewState。在以下情况下不再需要ViewState。
页面
控件
页面不回传给自身
处理的不是控件地事件
控件没有动态的或数据邦定的属性值(或对于每一个请求他们都设置在代码中)
关闭ViewState例子:
Default4.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default4.aspx.cs" Inherits="Default4" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>减少页面的“HTML 有效负载”</title>
</head>
<body>
<form id="form1" runat="server">
<h3>
通过禁用 ViewState 来减少页面的“HTML 有效负载”
</h3>
<p>
<asp:GridView ID="GridView" runat="server" BackColor="White" BorderColor="#3366CC" BorderStyle="None" BorderWidth="1px" CellPadding="4" EnableViewState="False">
<FooterStyle BackColor="#99CCCC" ForeColor="#003399" />
<RowStyle BackColor="White" ForeColor="#003399" />
<SelectedRowStyle BackColor="#009999" Font-Bold="True" ForeColor="#CCFF99" />
<PagerStyle BackColor="#99CCCC" ForeColor="#003399" HorizontalAlign="Left" />
<HeaderStyle BackColor="#003399" Font-Bold="True" ForeColor="#CCCCFF" />
</asp:GridView>
</p>
</form>
</body>
</html>
Default4.aspx.cs
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class Default4 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
DataSet ds = new DataSet();
ds.ReadXml(Server.MapPath("testdata.xml"));
this.GridView.DataSource = ds;
this.GridView.DataBind();
}
}
7. 禁用ViewState
在上述事例中,我通过将网络的EnableViewState属性设置为false禁用了ViewState。可以针对单个控件、整个页面或整个应用程序禁用ViewState,如下所示:
每个控件(在标记上)
<asp:GridView EnableViewState=”false”/>
在页面(在指令中)
<%@Page EnableViewState=”false”%>
每个应用程序(在web.config中)
<Pages EnableViewState=”false”/>
8. 使用ViewState更安全
由于ViewState没有被格式化为清晰的文本,某些人有时会认为它被加密了,其实并没有。相反,ViewState只是进行了Base64编码,以确保值在返回过程中不会发生变化,而且不考虑应用程序使用的相应/请求编码。可以向应用程序中添加2种ViewState安全级别:1、防篡改;2、加密。
1、 防篡改
尽管散列代码不能确保ViewState字段中实际数据的安全,但它能够显著降低有人通过ViewState骗过应用程序的可能性,即防止回传应用程序通常禁用输入的值。
可以通过设置EnableViewStateMac属性来显示ASP.NET向ViewState字段中追加一个散列代码:
<%@ Page EnableViewStateMac="true" %>
可以在页面级上设置EnableViewStateMac,也可以在应用程序级别上设置。在回传时,ASP.NET将为ViewState数据生成一个散列代码,并将其与存储在回传值中的散列代码进行比较。如果两处的散列代码不匹配,该ViewState数据将被丢弃,同时控件将恢复为原来的设置。
默认情况下,ASP.NET使用SHA1算法来生成ViewState散列代码,此外,也可以通过在machine.config文件中设置<machineKey>来选择MD5算法,如下所示:<machineKey validation="MD5" />
软件开发网 www.mscto.com
2、 加密
可以使用加密来保护ViewState字段中的实际数据值。首先,必须设置:EnableViewState=”true”,然后,将machineKey validation类型设置为3DES。这个指示ASP.NET使用Triple DES对称加密算法来加密ViewState值。<machineKey validation=”3DES”/>
9. Web领域中的ViewState安全性
默认情况下,ASP.NET将创建一个随机的验证钥匙,并存储在每个服务器的本地安全授权(LSA)中。要验证在另一台服务器上创建的ViewState字段,两台服务器的validationKey必须设置为相同的值。如果通过上述方式之一,对运行Web领域配置中的应用程序进行ViewState安全设置,则需要为所有服务器提供一个唯一的、共享的验证密钥。
Default5.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default5.aspx.cs" Inherits="Default5" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>无标题页</title>
</head>
<body>
<form id="form1" runat="server">
<h3>生成随机加密密钥</h3>
<p>
<asp:RadioButtonList ID="RadioButtonList" runat="server" RepeatDirection="horizontal">
<asp:ListItem Value="40">40-byte</asp:ListItem>
<asp:ListItem Value="128" Selected="True">128-byte</asp:ListItem>
软件开发网 www.mscto.com
</asp:RadioButtonList>
<asp:Button ID="Button" runat="server" Text="生成密钥" OnClick="Button_Click" />
</p>
<p>
<asp:TextBox ID="TextBox" runat="server" TextMode="multiLine" Rows="10" Columns="70" BackColor="#EEEEEE" EnableViewState="false">
复制并粘贴生成的结果
</asp:TextBox>
</p>
</form>
</body>
</html>
Default5.aspx.cs
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Security.Cryptography;
using System.Text;
public partial class Default5 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button_Click(object sender, EventArgs e)
{
int keylength = Int32.Parse(this.RadioButtonList.SelectedItem.Value);
//在此处放入用于初始化页面的用户代码
byte[] buff = new byte[keylength / 2];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
//该数组已使用密码增强的随机字节进行填充
rng.GetBytes(buff);
StringBuilder sb = new StringBuilder(keylength);
for (int i = 0; i < buff.Length; i++)
{
sb.Append(string.Format("{0:X2}", buff[i]));
}
this.TextBox.Text = sb.ToString();
}
}
10. 总结:
ASP.NET ViewState是一种新的状态服务,可供开发人员基于每个用户来跟踪UI状态。ViewState是在一个隐藏的窗体字段中来回传递状态,并将它直接应用于页面处理框架中。但效果却非常好,在基于Web的窗体中只需要编写并维护很少的代码。