ASP.NET 2.0会话状态概述
说明:本文绝大部分摘自MSDN文档,同时参考《ASP.NET与C#从入门到精通》(A.Russell Jones著)。《ASP.NET与C#从入门到精通》是一本关于ASP.NET 1.0的书籍,与《Programming ASP.NET》比较起来,它对ASP.NET各种技术的解释比较清晰透彻,而后者比较适合初学者,是典型的手把手教你如何做,一步一步的讲解,但是各种技术点到为止,想深入的了解细节的话,在书中往往找不到答案。
当用户在构成 Web 应用程序的不同 ASP.NET 页面之间导航时,ASP.NET 会话状态使您能够为用户存储和检索值。HTTP 是一种无状态协议,这意味着您的 Web 服务器将对页面的每个 HTTP 请求当作独立的请求进行处理;服务器不保留与先前请求所使用的任何变量值有关的信息。ASP.NET 会话状态将一个有限时间窗口内来自同一浏览器的请求标识为一个会话,并在该会话持续期间保留变量的值。(时间窗口的大小在web.config文件中进行配置。)
默认情况下,所有 ASP.NET 应用程序都启用 ASP.NET 会话状态。
会话状态的替代选项包括应用程序状态(请参见 Application 属性),它存储 ASP.NET 应用程序的所有用户均可访问的变量;System.Web.Profile 命名空间,它将用户值持久保留在数据存储区中,不使用超时限制让其过期;System.Web.Caching 命名空间,它将常用值存储在所有 ASP.NET 应用程序均可访问的内存中;ASP.NET System.Web.UI.WebControls,它将控件值持久保留在 ViewState 中;Cookies;QueryString;以及可使用 Form 集合通过 HTTP POST 访问的 HTML 表单中的字段。有关不同状态管理选项的比较,请参见 ASP.NET 状态管理建议。
会话标识符
会话由一个可以使用 SessionID 属性读取的唯一会话标识符标识。为 ASP.NET 应用程序启用会话状态时,将检查应用程序中每个页面请求是否有浏览器发送的 SessionID值。如果未提供任何 SessionID值,则 ASP.NET 启动一个新会话,然后将该会话的 SessionID随响应一起发送到浏览器。
默认情况下,SessionID值存储在 cookie 中(此cookie存储在内存中,而不是用户计算机的硬盘上。),但也可以配置应用程序,将 SessionID值存储在 URL 中,以实现一个“无 cookie”的会话。有关更多信息,请参见会话标识符。
只要一直使用相同的 SessionID值来发送请求,会话就被视为活动的。如果特定会话的请求发送间隔超过指定的超时值(以分钟为单位),则该会话被视为已过期。使用过期的 SessionID值来发送请求将导致启动一个新会话。
Demo:输出SessionID的值
//PrintSessionID.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="PrintSessionID.aspx.cs" Inherits="PrintSessionID" %>
<!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>打印会话ID</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Literal ID="ltSessionID" runat="server" Text="会话ID: "></asp:Literal>
<asp:Label ID="lblSessionID" runat="server" Text=""></asp:Label>
<br />
<asp:Button ID="btnAddSessVar" runat="server" Text="添加一个会话变量" OnClick="btnAddSessVar_Click" />
</div>
</form>
</body>
</html>
//PrintSessionID.aspx.cs
public partial class PrintSessionID : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
lblSessionID.Text = Session.SessionID;
}
protected void btnAddSessVar_Click(object sender, EventArgs e)
{
Session["NewSessionVar"] = "Hello";
}
}
解释:每次刷新页面时都会得到一个不同的SessionID。这是因为默认配置会使用使用基于cookie的会话状态,此ID只在一个有效的时间窗口内有效。如果希望在刷新浏览器时不改变此会话ID,最简单的方法是添加一个会话变量。在添加了一个会话变量后,来自同一个浏览器的请求的会话ID在超时之前都会保持不变。
另一种保持SessionID的方法是将 SessionID 值存储在 URL 中,以实现一个“无 cookie”的会话。只需要在web.config中<configuration><system.web>节中添加如下配置即可:
<sessionState cookieless="true" regenerateExpiredSessionId="true" timeout="30"></sessionState>
此时SessionID也会直接出现在URL中。
会话变量
会话变量存储在通过 System.Web.HttpContext.Session 属性公开的 SessionStateItemCollection 中。会话变量集合按变量名称或整数索引来进行索引。仅需通过名称引用会话变量即可创建会话变量。无需声明会话变量或将会话变量显式添加到集合中。默认情况下,会话变量可以为任何有效的 .NET 类型。例如,下面的代码示例将值的 ArrayList 存储在名为“StockPicks”的会话变量中。注意,由“StockPicks”会话变量返回的值在从 SessionStateItemCollection检索出来时必须强制转换为适当的类型。
注意 |
使用 InProc 以外的会话状态模式时,会话变量类型必须为基元 .NET 类型或可序列化的,原因是会话变量值存储在外部数据存储区中。有关更多信息,请参见会话状态模式。 |
C# |
|
// When retrieving an object from session state, cast it as // the appropriate type. ArrayList stockPicks = (ArrayList)Session["StockPicks"]; // Write the modified stock picks list back to session state. Session["StockPicks"] = stockPicks; |
由于Session对象中封装了包含对象的散列名称/值集合。可以将用C#创建的任何对象存储在会话变量中。
添加会话变量:Session[“counter”]=1; 或 Session.Add(“counter”, 1);
第二种方法更像标准的.NET集合代码。
会话事件
ASP.NET 提供两个有助于管理用户会话的事件:Session_OnStart事件(在开始一个新会话时引发)和 Session_OnEnd事件(在会话被放弃或过期时引发)。会话事件是在 ASP.NET 应用程序的 Global.asax 文件中指定的。注意,如果将会话 Mode 设置为 InProc(默认模式)以外的值,则 Session_OnEnd事件将不受支持。
注意 |
如果 ASP.NET 应用程序的 Global.asax 文件或 Web.config 文件被修改,将重新启动应用程序,而且存储在应用程序状态或会话状态中的所有值都将丢失。注意,某些防病毒软件可能会更新应用程序的 Global.asax 或 Web.config 文件的最后修改日期和时间。 |
有关更多信息,请参见会话状态事件。
会话模式
ASP.NET 会话状态支持会话变量的若干不同的存储选项。每个选项都被标识为一个会话状态 Mode。默认行为是将会话变量存储在 ASP.NET 辅助进程的内存空间中。但是,也可以指定将会话状态存储在单独进程、SQL Server 数据库或自定义数据源中。如果不希望为应用程序启用会话状态,可以将会话模式设置为 Off。
有关更多信息,请参见会话状态模式。
配置会话状态
使用 system.web配置节的 sessionState 元素来配置会话状态。还可以使用 EnableSessionState 页指令来配置会话状态。
使用 sessionState元素可以指定会话存储数据的模式、在客户端和服务器间发送会话标识符值的方式、会话 Timeout 值和基于会话 Mode的支持值。例如,下面的 sessionState元素将应用程序配置为 SQLServer 会话模式,Timeout为 30 分钟,并指定将会话标识符存储在 URL 中。
<sessionState mode="SQLServer" cookieless="true " regenerateExpiredSessionId="true " timeout="30" sqlConnectionString="Data Source=MySqlServer;Integrated Security=SSPI;" stateNetworkTimeout="30"/> |
可以通过将会话状态模式设置为 Off来禁用应用程序的会话状态。如果只希望禁用应用程序的某个特定页的会话状态,则可以将 EnableSessionState页指令设置为 false。注意,还可将 EnableSessionState页指令设置为 ReadOnly 以提供对会话变量的只读访问。
ASP.NET会话状态和可扩展性
微软通过使用独立的SessionState服务器(它作为一种服务存在)大大扩展了ASP.NET的会话状态。有四种可能的会话状态设置支配着ASP.NET用于存储会话数据的方法。
Off 表示应用程序不使用会话状态。即服务器不向客户端发送cookie或再生URL,而且也不能通过建立会话变量在页面之间保存数值。虽然仍然可以创建会话变量,但是服务器在请求处理完毕后将清除创建的变量。
InProc 表示会话在进程中,即服务器和应用程序运行在同一个进程中。当创建应用程序时,会话状态默认为InProc。这意味着对于多个服务器不能使用该设置。
StateServer ASP.NET有另一个服务器应用程序,称为SessionState服务器,它可以在独立的机器上运行。由于会话数据存储在独立的计算机上,所以可以扩展应用程序使其用于多个web服务器,而每个服务器负责在SessionState服务器上查询存储的会话数据。
SQLServer ASP.NET将会话数据存储在SQL Server数据库中。程序员不需要专门为数据库的建立做什么,ASP.NET将自动创建和扩展数据库,但仍然需要建立备份和恢复方法。
并发请求和会话状态
对 ASP.NET 会话状态的访问专属于每个会话,这意味着如果两个不同的用户同时发送请求,则会同时授予对每个单独会话的访问。但是,如果这两个并发请求是针对同一会话的(即,使用相同的 SessionID值),则接收到的第一个请求获得对会话信息的独占访问权,而第二个请求将在第一个请求完成之后执行,或直到由于第一个请求超过锁定超时时间而释放对该信息的独占锁定后再执行。如果将 EnableSessionState页指令设置为 ReadOnly,则对只读会话信息的请求不会导致对会话数据的独占锁定。可能仍需等到会话数据由读写请求而获得的锁定被解除后,对会话数据的只读请求才能得到满足。
会话状态管理
ASP.NET 提供了会话状态管理,使您可以根据多种请求存储与唯一浏览器会话相关联的信息。您可以存储由键名或数字索引引用的值的集合。您可以使用 HttpSessionState类访问会话值和功能,该类可通过当前 HttpContext 的 Session 属性或 Page 的 Session 属性进行访问。
会话数据通过唯一标识符与特定浏览器会话相互关联。默认条件下,该标识符存储在浏览器的不过期会话 Cookie 中,但是您也可以在应用程序配置的 sessionState 元素中,将 cookieless属性设置为 true或 UseUri,以此方法配置应用程序,将会话标识符存储在 URL 中。您可以通过指定 cookieless属性的 UseDeviceProfile 值,让 ASP.NET 确定浏览器是否支持 Cookie。您也可以为 cookieless属性指定 AutoDetect 值,让 ASP.NET 确定是否为浏览器启用 Cookie。如果指定 UseDeviceProfile后浏览器支持 Cookie,或指定 AutoDetect后浏览器启用了 Cookie,则会话标识符将存储在 Cookie 中;否则它将存储在 URL 中。
第一次请求过程中会话将会启动,并且在达到 Timeout 属性中指定的分钟数之前如果浏览器未发送新的请求,当前的会话值将持续有效。新会话开始后将引发会话 Start 事件。会话启动时您可以使用此事件执行任何其他工作,例如设置默认会话值。如果会话超时,将调用 Abandon 方法,或者关闭 ASP.NET 应用程序,此时将引发会话 End 事件。您可以使用此事件执行任何必要的清理操作。注意仅当会话状态 mode被设置为 InProc 时,才引发 End事件。
会话状态的持续性不会跨越 ASP.NET 应用程序的边界(例如你在访问网易几分钟后,转而访问搜狐)。如果浏览器定位到另一应用程序,则当前的会话信息对新应用程序不再有效。
默认条件下,会话值存储在 Web 服务器的内存中。您也可以把会话值存储在 SQL Server 数据库、ASP.NET 状态服务器或自定义服务器中。一旦 ASP.NET 或 IIS 进程或 ASP.NET 应用程序重新启动,这种举措可以保存会话值,并且它还可以使会话值在网络场的所有服务器间可用。要配置这种行为,请在应用程序配置的 sessionState 元素中把 mode属性设置为有效的 SessionStateMode 值。有关更多信息,请参见会话状态模式。
会话状态的替代项包括应用程序状态(请参见 Application 属性)和 ASP.NET 缓存(请参见 System.Web.Caching 命名空间),它们存储可由 ASP.NET 应用程序的所有用户访问的变量;ASP.NET 配置文件(请参见 System.Web.Profile 命名空间),它将用户值保持在数据存储区中,而不使用超时功能设置到期时间;ASP.NET System.Web.UI.WebControls,它将控件的值保持在 ViewState 中;Cookies;QueryString 属性;HTML 窗体上的字段,可使用 Form 集合通过 HTTP POST 使用这些字段。有关会话状态与其他状态管理替代项之间的区别的更多详细信息,请参见 ASP.NET 状态管理建议。浏览器的会话使用存储在 SessionID 属性中的唯一标识符进行标识。会话 ID 使 ASP.NET 应用程序能够将特定的浏览器与 Web 服务器上相关的会话数据和信息相关联。会话 ID 的值在浏览器和 Web 服务器间通过 Cookie 进行传输,如果指定了无 Cookie 会话,则通过 URL 进行传输。
警告 |
无论是作为 Cookie 还是作为 URL 的一部分,P:System.Web.SessionState.HttpSessionState.SessionID 值都是以明文形式发送的。有害源如果获取此 SessionID值并将其包含在对服务器的请求中,则可以访问另一位用户的会话。如果将私人信息或敏感信息存储在会话状态中,建议您使用 SSL 来加密浏览器和服务器之间包含 SessionID的任何通信。 |
无 Cookie SessionID
默认情况下,SessionID存储在浏览器未到期会话的 Cookie 中。通过在 Web.config 文件的 sessionState 节中将 cookieless属性设置为 TRUE,可以指定不将会话标识符存储在 Cookie 中。
注意 |
为提高应用程序的安全性,您应当允许用户从应用程序注销,此时应用程序应当调用 Abandon 方法。这降低了有害源获取 URL 中的唯一标识符并用它检索存储在会话中的用户私人数据的风险。 |
ASP.NET 通过自动在页的 URL 中插入唯一的会话 ID 来保持无 Cookie 会话状态。例如,下面的 URL 已被 ASP.NET 修改,以包含唯一的会话 ID lit3py55t21z5v55vlm25s55:
http://www.example.com/s(lit3py55t21z5v55vlm25s55)/orderform.aspx |
ASP.NET 修改所有请求页中包含的使用相对于应用程序的路径的链接(不修改显式路径),方法是在将每页发送到浏览器之前,在链接中嵌入一个会话 ID 值。只要用户遵循 ASP.NET 应用程序提供的链接路径,即可保持会话状态。但是,如果客户端重写了应用程序提供的 URL,ASP.NET 将不能解析此会话 ID,也不能将此请求与现有的会话相关联,因此会为此请求启动一个新的会话。
会话 ID 嵌入在 URL 中应用程序名称后的斜杠之后,在其余所有文件或虚拟目录标识符之前。这使 ASP.NET 可以在涉及请求中的 SessionStateModule 之前解析应用程序的名称。
下面的示例演示一个 Web.config 文件,它将 ASP.NET 应用程序配置为使用无 Cookie 会话标识符。
<configuration> <system.web> <sessionState cookieless="true" regenerateExpiredSessionId="true" /> </system.web> </configuration> |
重新生成已过期的会话标识符
默认情况下会回收无 Cookie 会话中使用的会话 ID 值。即如果使用已过期的会话 ID 发起一个请求,将使用此请求提供的 SessionID启动一个新的会话。如果一个包含无 Cookie SessionID 的链接被多个浏览器共享时(可能通过搜索引擎或其他程序),此行为可能导致对会话数据的意外共享。可以通过禁用会话标识符的回收来降低多个客户端共享会话数据的可能性。为此,将 sessionState 配置元素的 regenerateExpiredSessionId属性设置为 true。这样,在使用已过期的会话 ID 发起无 Cookie 会话请求时,将生成一个新的会话 ID。
注意 |
如果使用 HTTP POST 方法发起使用已过期会话 ID 的请求,则当 regenerateExpiredSessionId为 true时,将丢失发送的所有数据,因为 ASP.NET 会执行重定向,以确保浏览器在 URL 中具有新的会话标识符。 |
自定义会话标识符
可以实现自定义类来提供和验证 SessionID值。方法是创建一个类,使其继承 SessionIDManager 类,然后用您自定义的实现来重写 CreateSessionID 和 Validate 方法。有关重写 SessionIDManager类并实现这些方法的示例,请参见为 CreateSessionID方法提供的示例。
可以通过创建实现 ISessionIDManager 接口的类来替换整个 SessionIDManager。例如,有的 Web 应用程序可能将唯一标识符与非 ASP.NET 页(如 HTML 页或使用 ISAPI 筛选器的图像)相关联。可以实现自定义的 SessionIDManager类,将此唯一标识符用于 ASP.NET 会话状态。如果您的自定义类支持无 Cookie 会话标