状态管理是在相同或不同页面的多个请求之上维护状态和页面信息的过程。与任何基于 HTTP 技术的实质一样,Web 窗体页面是没有状态的,这意味着它们不会自动指出序列中的请求是否全部来自相同的客户端或者是否只是一个仍然在查看页面或网站的活动浏览器实例。此外,页面在每次在回传到服务器的过程中都被销毁并重建;因此,任何超出单个页面生命周期之外的信息都将不覆存在。关于服务器回传过程和 Web 窗体页面生命周期的更多信息,请参考:[ASP.NET 页面生命周期概览]。
ASP.NET 为在服务器回传过程之间的状态维护提供了多种方法。这些所选择的方法主要依赖于应用程序的需求,并且应该基于下列标准:
-
你需要存储多少信息?
-
客户端是否接受持续的或者内存中的 Cookies?
-
你需要把信息存储在客户端还是服务器?
-
是否是机密信息?
-
你的应用程序中的性能和带宽标准是什么?
-
你的目标浏览器和客户端设备需要怎样的能力?
-
你是否需要存储每个用户的信息?
-
你需要把信息存储多长时间?
-
你是否拥有 Web 农场(多个服务器)、Web 花园(一个机器中的多个进程)、还是只有单个进程来为应用程序服务?
客户端状态管理选项
使用客户端选项来存储页面信息的时候并不需要使用服务器的资源。这些选项通常安全性不高,但是因为只需要适量的服务器资源,所以服务器的性能比较高。但是,因为你必须把信息发送并存储到客户端,所以通过这种方式所存储的信息有着实际的数量限制。
下面是 ASP.NET 所支持的客户端状态管理选项:
视图状态
控件状态
隐藏字段
Cookies
查询字符串
视图状态
为了在相同页面的多个请求之间自动保持值,Web 窗体页面提供了一个 ViewState 属性以实现内建的结构。页面中的视图状态被当成隐藏字段进行维护。关于更多信息,请参考:[ASP.NET 状态管理概览]。
你可以在页面被回传到页面自身的时候在跨回传过程之间使用视图状态来存储特定页面的自定义值。例如,如果你的应用程序维护了特定的用户信息,也就是说,这些信息在页面中使用但是并不是任何控件的必需部分,那么你就可以把它们存储到视图状态中。
使用视图状态的优势
-
不需要服务器资源。视图状态被包含在页面代码的结构中。
-
容易实现。视图状态并不需要任何编程就可以使用。默认的时候它并不对控件的状态数据进行维护。
-
增强的安全特征。视图状态中的值是被混列的、被压缩的、并且进行了 Unicode 编码,从而比隐藏字段提供了更多的安全性。
使用视图状态的缺点
-
性能考虑。因为视图状态被存储在页面中,存储庞大的值能够导致页面在用户显示和提交的时候会影响上传下载的速度。尤其是那些带宽有限的移动设备。
-
设备的局限性。移动设备可能没有足够的内存来存储这些数量庞大的视图状态数据。
-
潜在的安全隐患。视图状态被存储在页面中的多个隐藏字段中。尽管视图状态对需要存储的数据进行混列格式化,但是它仍然能够被篡改。如果页面的输出源能够被直接查看,那么隐藏字段中的信息也能够被查看到,这引起了一个潜在的安全问题。关于更多信息,请参考:[ASP.NET Web 应用程序的安全]以及[Web 应用程序的基本安全练习]。
关于使用视图状态的更多信息,请参考:[视图状态概览]。
控件状态
ASP.NET 页面框架提供了 ControlState 属性在服务器回传过程之间存储自定义控件的数据。例如,如果你编写了一个使用不同制表页显示不同信息的自定义控件,要让控件能够像预期的那样正常工作,控件就需要在回传过程之间知道哪一个制表页是被选中的。视图状态就能够用于这个目的,但是开发者能够在页面级别把视图状态关闭,而影响控件的正常功能。与视图状态不一样,控件状态不能够被关闭,所以它为控件状态数据的存储提供了更可靠的方式。
使用控件状态的优势
-
不需要服务器资源。默认时,控件状态被存储在页面中的隐藏字段中。
-
可靠性。因为控件状态不像视图状态那样能够被关闭,所以控件状态是更可靠的控件状态管理方式。
-
多功能性。能够为控件编写自定义适配器来确定控件状态数据被存储的方式和位置。
使用控件状态的缺点
-
需要编程。ASP.NET 页面框架为控件状态提供了一个基础,控件状态是一个自定义的状态保持机制。要充分地对控件状态进行利用,你必须编写代码来保存并载入控件的状态。
隐藏字段
为了维护页面的状态,你可以把特定的页面信息存储到页面的隐藏字段中。关于隐藏字段的更多信息,请参考:[ASP.NET 状态管理的建议]。
如果你使用了隐藏字段,最好是只存储客户端的少量经常被改变的数据。
提示:如果你使用了隐藏字段,你必须使用 HTTP POST 方法把页面提交到服务器,而不是通过页面 URL (HTTP GET 方法)来请求页面。
使用隐藏字段的优势
-
不需要服务器资源。隐藏字段被存储在页面中并从页面中读取。
-
广泛的支持。几乎所有浏览器和客户端设备都支持包含隐藏字段的窗体。
-
容易实现。隐藏字段是标准的 HTML 控件,且不需要编写复杂的编程逻辑。
使用隐藏字段的缺点
-
潜在的安全隐患。隐藏字段能够被篡改。隐藏字段中的信息能够在页面的输出源被直接查看的时候而暴露,引起一个潜在的安全隐患。你可以对隐藏字段的内容进行手动加密和解密,但是这样做需要额外的编程以及维护。如果涉及到安全问题,请考虑使用基于服务器的状态机制,以避免把机密信息发送到客户端。关于更多信息,请参考:[ASP.NET Web 应用程序的安全]以及[Web 应用程序的基本安全练习]。
-
简易的存储结构。隐藏字段不支持富数据类型。隐藏字段提供一个单一的字符串值字段来存放信息。要存储多个值,你必须实现被分界的字符串并且编写代码进行处理。你能够分别在隐藏字段中对富数据类型进行手动的序列化和解序列操作。但是,这需要编写额外的代码。如果你需要把富数据类型保存到客户端,考虑使用视图状态作为替代。视图状态拥有内建的序列化功能,并且它同样能够把数据存储到隐藏字段中。
-
性能考虑。因为隐藏字段是被存储在页面中的,所以存储庞大的值能够在用户显示页面和提交页面的时候影响上传下载的速度。
-
存储的局限性。如果隐藏字段中的数据非常庞大,有些代理和防火墙会对这些包含庞大数据的页面访问进行限制。因为数量的限制能够随不同的防火墙和代理的实现而变化,庞大的隐藏字段会导致零散的数据碎片。如果你需要存储大量的数据,请考虑下列操作:
-
把每个信息项放进单独的隐藏字段中。
-
与视图状态拆分一起使用视图状态,视图状态拆分特征能够自动地把数据拆分到多个隐藏字段中。
-
不把数据存储到客户端,而是在服务器上保持这些数据。你发送到客户端的数据越多,应用程序的响应时间就会变得越慢,这是因为浏览器需要下载或上传更多的数据量。
-
Cookies
Cookies 用于存储少量经常在客户端被改变的信息。这些信息连同请求一起被发送到服务器。关于创建并读取 Cookies 的详细内容,请参考:[ASP.NET Cookies 概览]。
使用 Cookies 的优势
-
可配置的过期规则。Cookies 能够在浏览器会话结束的时候过期,或者能够不定期地存在于客户端的计算机中,并遵循客户端计算机中的过期规则。
-
不需要服务器资源。Cookies 被存储在客户端并且在服务器接受请求之后被读取。
-
简单。Cookies 是轻量级的、基于文本的简单 Key/Value 数据对结构。
-
保持数据。虽然客户端计算机中 Cookies 的保存期遵循客户端的 Cookies 过期进程和用户的干预,但是 Cookies 仍然经常被用来在客户端保持窗体的数据。
使用 Cookies 的缺点
-
长度的局限性。尽管支持 8192 字节的 Cookies 长度在较新的浏览器和客户端设备的版本中变得流行起来,但是大部分浏览器还是作出了 4096 字节的 Cookies 长度限制。
-
用户所配置的取舍权。有些用户能够为他们的浏览器或客户端设备禁用接收 Cookies 的能力,因此限制这些功能的使用。
-
潜在的安全隐患。Cookies 也能够被篡改。用户能够在他们的计算机中操作 Cookies,从而潜在地导致一个安全隐患或者导致依赖于 Cookies 的应用程序出现错误。同样,虽然 Cookies 只能够由创建并发送到客户端的域所访问,但是黑客能够从用户的计算机中访问属于其他域的 Cookie。你可以手动地对 Cookies 进行加密和解密,但是这样做需要额外的编程并会影响到应用程序的性能(因为需要时间来执行加密和解密的过程)。关于更多信息,请参考:[ASP.NET Web 应用程序的安全]以及[Web 应用程序的基本安全练习]。
提示:Cookies 经常被用于个性化,并且内容能够被一个已知的用户进行定制。在多数的此类情况中,识别是比验证更关键的问题。因此,你能够典型地对一个通过存储用户名称、帐号名称、或者是唯一的用户 ID(如一个 GUID)而用于识别的 Cookie 进行保护,然后使用这个 Cookie 来访问用户在网站中的个性化结构。
查询字符串
查询字符串是被添加在页面 URL 未端的信息。关于更多信息,请参考:[ASP.NET 状态管理概览]。
你可以使用查询字符串把数据提交回页面本身或者通过页面 URL 提交到其他页面。查询字符串为维护一些状态信息而提供了一个简单的但是有限的方式。例如,查询字符串在从一个页面传递信息到另一个页面的时候是很轻松的,如把一个产品编号传递到另一个将对编号进行处理的页面。
使用查询字符串的优势
-
不需要服务器资源。查询字符串被包含在特定 URL 的 HTTP 请求中。
-
广泛的支持。几乎所有浏览器和客户端设备都支持使用查询字符串来传递参数。
-
容易实现。ASP.NET 完全支持查询字符串方法,包括使用 HttpRequest 对象的 Params 参数来读取查询字符串的内容。
使用查询字符串的缺点
-
潜在的安全隐患。查询字符串中的信息能够直接显示在浏览器的用户界面中。用户能够对 URL 进行标记或者发送到其他用户,因此查询字符串中的信息也就连同一起被标记或转发。如果你在查询字符串中使用了任何机密数据,请考虑使用在窗体中使用隐藏字段并使用 POST 方法来代替查询字符串。关于更多信息,请参考:[ASP.NET Web 应用程序的安全]以及[Web 应用程序的基本安全练习]。
-
有限的能力。有些浏览器和客户端设备限制了 URL 中最多只能拥有 2083 个字符。
客户端状态管理方法的概述
下表列出了 ASP.NET 中可用的客户端状态管理选项,并提供了关于何时应该使用相应选项的建议。
状态管理选项 | 建议用法 |
---|---|
视图状态 | 在你需要对将被回传到页面本身的少量页面信息进行存储的时候使用。使用 ViewState 属性来提供基本的安全功能。 |
控件状态 | 在你需要在服务器的回传过程之间对控件的少量状态信息进行存储的时候使用。 |
隐藏字段 | 在你需要对将被回传到页面本身或者其他页面的少量页面信息进行存储的时候使用,并且不用考虑安全问题。 提示:你只能在被提交到服务器的页面中使用隐藏字段。 |
Cookies | 在你需要在客户端对少量信息进行存储的时候使用,并且不用考虑安全问题。 |
查询字符串 | 在你从一个页面到另一个页面传递少量信息的时候使用,不用考虑安全问题。 提示:你只能在请求相同页面、或者通过连接请求另一个页面的时候才可以使用查询字符串。 |
服务器端状态管理选项
存储页面信息的服务器端选项通常拥有比客户端选项更高的安全性,但是它们能够使用更多的 Web 服务器资源,从而在被存储的信息变得十分庞大的时候能够直接影响到应用程序的伸缩性。ASP.NET 提供了几个实现服务器端状态管理的选项。关于更多信息,请参考:[ASP.NET 状态管理概览]。
以下是 ASP.NET 所支持的服务器端的状态管理选项:
应用程序状态
会话状态
档案属性
数据库支持
应用程序状态
ASP.NET 通过 HttpApplicationState 类提供应用程序状态来存储全局的应用程序信息,这些信息在整个应用程序中可见。应用程序状态变量是 ASP.NET 应用程序中最有效的全局变量。关于更多信息,请参考:[ASP.NET 应用程序状态概览]。
你能够把特定的应用程序值保存到应用程序状态中,然后由服务器所管理。关于更多信息,请参考:[ASP.NET 状态管理概览]。
数据通过多个会话被共享并且经常在数据类型被插入到应用程序状态变量中的时候保证内容不会被改变。
使用应用程序状态的优势
-
容易实现。应用程序状态容易使用、被 ASP 开发者所熟悉、并且与其他 .NET Framework 类保持一致性。
-
作用于应用程序范围。因为应用程序状态能够被应用程序中的所有页面所访问,在应用程序状态中存储信息能够确保只出现相关信息的单个拷贝(例如,反对在会话状态或单独页面中保持信息的副本)。
使用应用程序状态的缺点
-
作用于应用程序范围。应用程序状态的作用范围同样也是一个缺点。被存储在应用程序状态中的变量只针对运行在该应用程序中的特定进程才是全局性的,并且在不同的应用程序进程中能够拥有不同的值。因此,你不能依赖应用程序状态来存储唯一的值或者更新 Web 花园和 Web 农场服务器配置中的全局计数器。
-
有限的数据保存期限。因为被存储在应用程序状态中的全局数据是不稳定的,它将会在包含它的 Web 服务器进程被销毁的时候丢失所有内容,如服务器重启、升级、或者关闭的时候。
-
需要资源。应用程序状态需要使用服务器中的内存,从而能够影响到服务器的性能以及应用程序的可伸缩性。
请小心地设计并实现能够增加 Web 应用程序性能的应用程序状态。例如,把经常被使用的静态数据集保存到应用程序状态中虽然能够增加网站的性能,并减少对数据库的请求次数。但是,这仍然会出现性能交换。应用程序状态变量所包含的庞大信息块会像服务器负荷的增长一样影响到 Web 服务器的性能。存储在应用程序状态中的变量所占用的内存在变量的值被删除或者被取代之前是不会被释放的。因此,存储在应用程序状态中的变量最好是小巧的、且很少被改变的数据集。关于更多信息,请参考:[开发高性能的 ASP.NET 应用程序]。
会话状态
ASP.NET 提供了一个会话状态,即一个可用的 HttpSessionState 类,用来存储只在会话中可见的特定会话信息。ASP.NET 会话状态可以在会话的有效期间对于来自于相同浏览器的请求进行识别,并且为会话期间提供保持变量值的能力。关于更多信息,请参考:[ASP.NET 状态管理概览]以及[会话状态概览]。
你可以把特定的会话值和对象存储到会话状态中,然后它们将被服务器所管理并且对于浏览器或客户端设备可用。存储在会话状态变量中的数据的有效时间是短暂的,并且机密数据会被指定到单独的会话。
使用会话状态的优势
-
容易实现。会话状态容易使用、被 ASP 开发者所熟悉、并且与其他 .NET Framework 类保持一致性。
-
特定的会话事件。会话所管理的事件能够被触发并且在应用程序中被使用。
-
保持数据。存放在会话状态变量中的数据能够得到保护,不会在 Internet 信息服务(IIS)被重启或者工作者进程被重启而丢失会话数据,因为这些数据被存储在另一个进程空间中。另外,会话状态数据能够跨多个进程被保持,如在 Web 农场或者 Web 花园中。
-
平台可伸缩性。会话状态能够同时在多计算机和多进程配置中使用,从而优化了可伸缩性。
-
无 Cookies 支持。会话状态能够与不支持 HTTP Cookies 的浏览器一起工作,虽然会话状态通常与 Cookies 一起被用来为 Web 应用程序提供对用户的识别功能。使用没有 Cookies 的会话状态仍然需要把会话标识符存放到查询字符串中,这样就会出现本文在查询字符串部分所讨论的安全问题。关于使用无 Cookies 会话状态的更多信息,请参考:[配置 ASP.NET 应用程序]。
-
可扩展性。你能够编写属于你自己的会话状态提供者来自定义并扩展会话状态。这时候的会话状态数据能够被存储到自定义的数据格式中,包括多种数据存储机制,如数据库、XML 文件、或者 Web Service。关于更多信息,请参考:[实现会话状态存储提供者]。
使用会话状态的缺点
-
性能考虑。会话状态变量在它们被删除或者被取代之前始终都位于内存中,并且因此能够降低服务器的性能。会话状态变量所包含的信息块,如庞大的数据集,能够与服务器负荷的增长一样影响 Web 服务器的性能。
档案属性
ASP.NET 提供了一个被称作档案属性的特征,允许你保存特定的用户数据。这个特征与会话状态类似,不同之处就是档案数据不会在用户会话过期的时候被丢失。档案属性特征所使用的 ASP.NET 档案被保存在一个持续格式中并且与单独的用户相关联。ASP.NET 档案允许你在不需要创建并维护自定义数据库的情况下来简化对用户信息的管理。另外,档案还使用一个强类型的 API 使用户信息能够生效,并且你能够在应用程序的任何位置对这个 API 进行访问。你能够在档案中保存任何类型的对象。ASP.NET 档案特征提供了一个普通的存储系统来允许你在仍然需要在类型安全规则中使数据生效的情况下定义并维护几乎所有类型的数据。关于更多信息,请参考:[ASP.NET 档案属性概览]。
使用档案属性的优势
-
保持数据。存放在档案属性中的数据是被保护的,不会在 IIS 被重启或者工作者进程被重启的时候而丢失数据,因为数据被保存在外部机制中。另外,档案属性还能够在跨进程之间被保持,如在 Web 农场或者 Web 花园中。
-
平台可伸缩性。档案属性既能够在多计算机配置中也能够在多进程配置中被使用,从而优化了可伸缩性。
-
可扩展性。要使用档案属性,你必须先配置一个档案提供者。ASP.NET 包括一个允许你在 SQL 数据库中存储档案数据的 SqlProfileProvider 类,但是你同样能够创建自定义的档案提供者类,把档案数据以自定义的格式存储到一个自定义格式和自定义存储机制中,如 XML 文件、或者甚至是 Web Service。关于更多信息,请参考:[ASP.NET 档案提供者]以及[实现档案提供者]。
使用档案属性的缺点
-
性能考虑。档案属性的处理通常比使用会话状态要慢,那是因为档案属性并不是存储在内存中的,而是保持在数据存储中。
-
额外的配置需求。与会话状态不同,档案属性特征需要使用相当多的配置。要使用档案属性,你不仅需要配置一个档案提供者,而且还必须对需要存储的所有档案属性进行预先配置。关于更多信息,请参考:[ASP.NET 档案属性概览]以及[定义 ASP.NET 档案属性]。
-
数据维护。档案属性需要进行维护。因为档案数据被保持在稳定的存储中,在数据开始失效的时候,你必须确保你的应用程序调用了适当的清除机制(这些机制提供自档案提供者)。
数据库支持
在有些情况中,你可能需要在网站中使用数据库支持来对状态进行维护。通常,数据库支持被用于 Cookies 或会话状态的联合中。例如,电子商务网站经常会出于下列原因而使用相关的数据库来维护状态信息:
安全性
个性化
一致性
数据采集
以下是一个支持 Cookies 的网站数据库的典型特征:
-
安全性。访问者在网站登入页面中输入帐号名称和密码。网站结构会使用登入的值来查询数据库以确定该用户是否拥有网站的使用权限。一旦数据库确认了用户信息,网站就会为该用户在客户端计算机中分配一个包含唯一 ID 的有效 Cookie。同时网站准许用户进行访问。
-
个性化。适当地使用安全信息,你的网站能够通过读取客户端计算机中的 Cookie 来区别每一个用户。通常,网站的数据库中拥有描述用户偏好的信息(由一个唯一的 ID 来识别)。这个关系就是所谓的个性化。网站能够使用这个被包含在 Cookie 中的唯一 ID 来调查用户的偏好,然后把内容和信息按照用户所指定的意愿进行呈现,以随时对用户的偏好作出响应。
-
一致性。如果你已经创建了一个商业网站,你可能需要把购买货物和服务的事务记录保持到网站中。这些信息能够被可靠地保存到数据库中并且通过用户的唯一 ID 进行引用。它能够用来检测交易事物是否已经完成,并且在事件出现错误的时候对动作的过程进行检测。这些信息同样能够用于把订单的状态通知给用户。
-
数据采集。与网站使用、访问者、或者产品事务相关的这些信息都能够可靠地被保存到数据库中。例如,你的业务开发部门可能需要从网站中所收集的数据来确定下一年的产品线或销售策略。你的行销部门可能需要对网站用户的统计信息进行调查。你的工程师和售后支持部门可能需要对那些需要改良的交易事务处理进行考察并记录。大部分企业级相关的数据库(如 Microsoft SQL Server),都为大部分数据采集工程包含有极易扩展的工具集。
通过把网站设计成在上述情景中的每个通用场景期间都使用唯一 ID 来对数据库进行反复查询,网站就能够对状态进行维护。同样,用户也会感觉到网站已经记住他们并且能够作出反应。
使用数据库来管理状态的优势
-
安全性。访问数据库时需要严格的验证和授权。
-
存储能力。你能够在数据库中存储任意数量的信息。
-
数据保持。数据库信息能够被存储任意长的时间,并且不需要遵循 Web 服务器的可用性原则。
-
健壮和数据完整性。数据库包括不同的设施来维护良好的数据,包括触发器和引用的完整性、事务、等等。通过在数据库中(例如,胜于在会话状态中)保持事务的相关信息,并且你更加容易地从错误中进行恢复。
-
可访问性。存储在数据库中的数据能够通过多种信息处理工具进行访问。
-
广泛的支持。有非常多的数据库工具可以使用,并且多数的自定义配置也是可用的。
使用数据库来维护状态的缺点
-
复杂性。使用数据库来支持状态管理会使得硬件和软件的配置变得更加复杂。
-
性能考虑。构造劣质的关系数据模型能够导致伸缩性问题。同样,过多地在数据库中使用查询也能够影响服务器的性能。
服务器端状态管理方法的概述
下表列出了 ASP.NET 中可用的服务器端状态管理选项,并提供了关于何时应该使用相应选项的建议。
状态管理选项 | 建议用法 |
---|---|
应用程序状态 | 在你存储极少改变的、被多个用户使用的全局信息的时候使用。请不要在应用程序状态中存储大量的信息。 |
会话状态 | 在你存储指定给单独会话并且需要考虑安全问题的短暂信息的时候使用。不要在会话状态中存储大量的信息。你应该了解到会话状态对象将在应用程序每个会话的生命周期中被创建并维护。在托管多数用户的应用程序中,就会占用重要的服务器资源并且影响可伸缩性。 |
档案属性 | 在你存储需要在用户会话过期之后(并且需要重新从应用程序的后续访问中获取)被保持的特定用户信息的时候使用。 |
数据库支持 | 在你存储大量信息、管理事务、或者信息且必须避免应用程序和会话被重启的时候使用。数据采集是被关注的,并且安全性也是一个问题。 |