视图状态是 ASP.NET 页框架用于在
往返过程之间保留页和控件值的方法。在呈现页的 HTML 标记时,必须在
回发过程中保留的页和值的当前状态将被序列化为 Base64 编码字符串。然后,此信息将被放入到一个或多个视图状态隐藏字段中。
简介
ASP.NET 视图状态概述 .NET Framework 4
本主题包含以下各节:
方案
功能
背景
类参考
方案
视图状态由 ASP.NET 页
框架自动用于保存在各个
回发之间必须保留的信息。此信息包括控件的任何非默认值。
您还可以使用视图状态来存储特定于页的应用程序数据。
功能
视图状态是 ASP.NET 页中的存储库,可以存储必须在
回发过程中保留的值。页框架使用视图状态在各个
回发之间保存控件设置。
可以在您自己的应用程序中使用视图状态完成以下工作:
在各个
回发之间保存值,而不将这些值存储在会话状态或
用户配置文件中。
存储您定义的页或控件属性的值。
创建一个自定义视图状态提供程序,以便将视图状态信息存储在 SQL Server 数据库或其他数据存储区中。
例如,您可以将信息存储在视图状态中,这样在下次将该页发送到服务器时,代码便可以在页加载事件过程中访问这些信息。
背景介绍
Web 应用程序是无状态的。每次从服务器请求页时,都会创建网页类的一个新实例。这通常意味着在每次
往返过程中会丢失该页及其控件中的所有信息。例如,默认状态下,如果用户将信息输入到 HTML 网页上的
文本框中,该信息会发送到服务器。但是,该信息不会在响应中返回到浏览器。
为了克服 Web 编程的这一固有的局限性,ASP.NET 页框架包含几种状态管理功能,可以在
往返过程之间将页和控件值保存到 Web 服务器。其中一种功能便是视图状态。
默认情况下,ASP.NET 页框架使用视图状态在
往返过程之间保存页和控件值。在呈现页的 HTML 时,必须在回发过程中保留的页和值的当前状态将被序列化为 Base64 编码字符串。然后,它们将被放入页中的一个或多个隐藏字段。
您可以在代码中使用页的 ViewState属性访问视图状态。ViewState属性是一个包含键/值对(其中包含视图状态数据)的字典。
通过实现自定义的 PageStatePersister类以
存储页数据,您可以更改默认行为并将视图状态存储到另一个位置(如 SQL Server 数据库)。有关将页状态存储在流中而不是隐藏字段中的示例,请参见 PageStatePersister类的示例。
注意事项
视图状态提供了特定 ASP.NET 页的状态信息。如果需要在多个页上使用信息,或者需要在对网站的多次访问之间保存信息,必须使用其他方法来维护状态。
视图状态信息被序列化为 XML,然后使用 base-64 编码机制进行编码,这可能会生成大量数据。将页发送到服务器时,视图状态的内容会作为页
回发信息的一部分进行发送。如果视图状态包含大量信息,则会影响页的性能。请使用应用程序的典型
数据测试页性能,以确定视图状态的大小是否会导致性能问题。
另一个注意事项是,如果隐藏字段中的数据量过大,则某些代理和防火墙将禁止访问包含这些数据的页。由于最大允许数量可能随所实现的防火墙和代理的不同而不同,因此大的隐藏字段可能会导致间歇性问题。如果存储在 ViewState属性中的数据量超出了在页的 MaxPageStateFieldLength属性中指定的值,则页会将视图状态拆分为多个隐藏字段。这可减小单个隐藏字段的大小,使其大小不会超过防火墙所允许的上限。
在默认情况下,将启用视图状态,但页上的某些控件可能不需要视图状态。例如,如果某个控件在每次
回发时都会从
数据存储区进行刷新,则可以关闭该控件的视图状态以减少视图状态的大小。
您可以对控件进行配置,以便默认情况下禁用页或
容器控件中所有控件的视图状态,然后可以为特定控件启用视图状态。您还可以对控件进行配置,以便禁用视图状态并且无法为子控件启用视图状态。
若要在默认情况下禁用控件的视图状态,以便可以为子控件启用视图状态,请将控件的 ViewStateMode属性设置为 Disabled。若要在默认情况下禁用整页的视图状态,请将 @
Page指令的 ViewStateMode特性设置为 Disabled。
若要禁用控件及其子控件的视图状态,以便无法为子控件启用视图状态,请将控件的 EnableViewState属性设置为 false。若要禁用整页及其所有子控件的视图状态,请将 @
Page指令的 EnableViewState特性设置为 false。
某些移动设备根本不允许使用隐藏字段。
控件状态
除视图状态以外,ASP.NET 还支持控件状态。页使用控件状态来保留必须在
回发之间保留的控件信息,即使已为页或某个控件禁用视图状态也是如此。控件状态与视图状态类似,也存储在一个或多个隐藏字段中。
状态保存
您可以使用页的 ViewState属性(该属性公开一个字典对象)来访问视图状态信息。可以使用此字典存储自定义值。一个典型用法是存储您在页中定义的
自定义属性的值。
由于视图状态是作为隐藏字段发送的,因此直到该页的 PreRenderComplete事件发生之前,都可以对视图状态进行更改。在将页呈现到浏览器之后,便无法保存对视图状态所做的更改。
如果用户查看网页的源并可以对 base-64 编码字符串进行解码,则可以看到隐藏视图状态字段中的信息。这可能会产生安全问题。
若要将值保存到视图状态,请创建一个包含要保存的值的新项,并将该项添加到视图状态字典中。下面的示例演示一个 ASP.NET 网页,该网页包含用于将一个字符串和一个整数值保存在视图状态中的代码。
C#
VB
可以存储在视图状态中的
数据类型 您可以将下列类型的对象存储到视图状态中:
字符串
整数
Boolean值
Array对象
ArrayList对象
自定义类型转换器(有关更多信息,请参见 TypeConverter类)
您还可以存储其他类型的数据,但是必须使用 Serializable特性编译类,以便可以为视图状态序列化该类的值。
状态取值
若要从视图状态读取值,请获取页的 ViewState属性,然后从视图状态字典中读取该值。
下面的示例演示如何从视图状态中获取一个名为 arrayListInViewState的 ArrayList对象,然后将一个 GridView控件作为
数据源绑定到该对象。
视图状态中的值被类型化为 String。在 Visual Basic 中,如果设置了 Option Strict On,则必须在使用视图状态值之前将这些值强制转换为适当的类型,如上面的示例中所示。在 C# 中,当您读取视图状态值时,必须始终将其强制转换为适当的类型。
如果尝试从不存在的视图状态中获取值,则不会引发任何异常。若要确保值在视图状态中,请首先检查对象是否存在。下面的示例演示如何检查视图状态项。
如果您尝试通过其他某种方式使用不存在的视图状态项(如检查其类型),则会引发 NullReferenceException异常。
安全保证
默认情况下,视图状态数据存储在页上的隐藏字段中,并使用 Base64 编码机制进行编码。此外,还会使用计算机
身份验证代码 (MAC) 密钥从视图状态数据中创建这些数据的哈希。
哈希值会添加到编码的视图状态数据中,并且生成的字符串会存储在页中。当页被
回发到服务器时,ASP.NET 页框架会重新计算
哈希值,并将其与视图状态中
存储的值进行比较。如果
哈希值不匹配,将引发异常,指示视图状态数据可能无效。
通过创建
哈希值,ASP.NET 页
框架可以测试视图状态数据是否已被损坏或篡改。但是,即使视图状态数据未被篡改,这些数据仍然可能被恶意用户截获和读取。
使用 MAC 计算视图状态
哈希值 用于计算视图状态哈希值的 MAC
密钥既可以自动生成,也可以在 Machine.config 文件中指定。如果该密钥是自动生成的,则基于计算机的 MAC 地址(它是该计算机中
网络适配器的唯一 GUID 值)进行创建。
恶意用户很难根据视图状态中的
哈希值进行反向工程处理以推断出 MAC 密钥。因此,MAC 编码是一种用来确定视图状态数据是否已更改的相当可靠的方式。
通常,用于生成哈希的 MAC
密钥越大,不同字符串的
哈希值相同的可能性就越小。如果
密钥是自动生成的,则 ASP.NET 使用 SHA-1 编码来创建一个大型密钥。不过,在网络场环境中,所有服务器的
密钥必须相同。如果
密钥不同,那么当页
回发至创建该页的服务器之外的其他服务器时,ASP.NET 页框架将引发异常。因此,在网络场环境中,应在 Machine.config 文件中指定
密钥,而不是让 ASP.NET 自动生成密钥。在这种情况下,请确保您创建的
密钥足够长,以便使
哈希值具有充分的安全性。但是,
密钥越长,创建哈希所需要的时间也就越多。因此,必须在安全需求与性能需求之间进行权衡。
加密视图状态 虽然 MAC 编码有助于防止篡改视图状态数据,但它无法阻止用户查看数据。可以通过下面两种方式来防止他人查看此数据:通过 SSL 传输页,以及对视图状态数据进行加密。要求通过 SSL 发送页有助于防止那些原本不应该收到该页的人探查
数据包和未经授权访问数据。
但是,请求该页的用户仍然能够查看视图状态数据,因为 SSL 会解密该页以便在
浏览器中显示它。如果您不担心授权用户可以访问视图状态数据,则这种方法很好。但在某些情况下,控件可能会使用视图状态
存储任何用户都不应访问的信息。例如,页可能包含一个
数据绑定控件,该控件存储视图状态的项标识符(数据密钥)。如果这些标识符中包含敏感数据(如客户 ID),则应对视图状态数据进行加密来替代通过 SSL 发送页,或是将其作为通过 SSL 发送页的补充方法。
若要加密数据,请将页的 ViewStateEncryptionMode属性设置为 true。在视图状态中存储信息时,可以使用常规的读写技术;页会为您处理所有加密和解密工作。对视图状态数据进行加密可能会影响应用程序的性能。因此,如不需要,请不要使用加密。
控件状态加密 使用控件状态的控件可以通过调用 RegisterRequiresViewStateEncryption方法来要求对视图状态进行加密。如果页中的任何控件都要求对视图状态进行加密,则该页中的所有视图状态都会进行加密。
基于每个用户的视图状态编码 如果网站需要对用户进行
身份验证,则可以设置 Page_Init事件处理程序中的 ViewStateUserKey属性,以便将页的视图状态与特定用户相关联。这将有助于防止一键式 (one-click) 攻击,在这种方式的攻击中,恶意用户创建一个有效的预先填充的网页,该网页具有来自以前创建的网页的视图状态。攻击者随后引诱受害者单击一个链接,该链接使用受害者的标识向服务器发送页。
如果设置了 ViewStateUserKey属性,将使用攻击者的标识来创建原始页的视图状态的哈希。受害者被引诱重新发送此页时,由于用户
密钥不同,因此
哈希值也将不同。这样,页的验证将失败,并且引发一个异常。
必须将 ViewStateUserKey属性与每个用户的一个唯一值(如用户名或标识符)相关联。
在共享承载环境中保护配置的安全性 在共享的承载环境中,恶意用户可能会修改状态管理属性,从而可能影响到计算机上的其他应用程序。修改方式包括:直接修改 Machine.config;使用配置类进行修改;以及使用其他管理和配置工具进行修改。您可以通过对配置文件的节进行加密来帮助防止他人修改您的应用程序配置。
类参考