那些刚刚进入Web编程的开发人员,总是难以理解维护应用程序状态的问题。万维网最基础的协议是HTTP协议是一个无状态协议,也就是说,从Web服务器端的角度看,每一个访问请求都是一个新的用户。HTTP协议不提供任何方法来决定两个访问请求是否是同一个用户发出的。
然而,维护状态对任何Web应用程序来说都是非常重要的。
在ASP.NET 2.0 Framework中,包含以下三种在多个页面请求间关联数据和特定用户的方法:
浏览器cookie:浏览器cookie用于关联一些文本到网站的每一个用户。
会话状态(session state):会话状态用于关联任意类型的对象到任何用户。
Profile对象:profile对象提供一个创建强类型和持久化方式的会话状态的方法。
使用浏览器cookie
Cookie是如何工作的呢?当Web服务器端创建一个cookie时,一个附加的HTTP首部在浏览器显示页面时被发送到浏览器。HTTP首部类似如下格式:
Set-Cookie:message=Hello
Set-Cookie首部使得浏览器创建一个名为message的cookie,包含值Hello。
在浏览器创建cookie后,它从相同的应用程序请求页面时,都将像下面这样发送这个HTTP首部:
Cookie:message=Hello
Cookie头包含所有的Web服务器端设置的cookie。每次浏览器向Web服务器端请求页面时,cookie都会发送回服务器端。
注意,一个cookie就是一段文本。Cookie只能用于存储字符串值。
实际上,我们可以存储两种类型的cookie:会话cookie和持久化cookie。一个会话cookie只存在于内存中。当用户关闭浏览器时,会话cookie就永远消失了。
而持久化cookie可以存在几个月设置几年。当创建一个持久化cookie时,cookie被浏览器长久存在用户的电脑上。以IE为例,cookie以一组文本文件的形式保存在下面的文件夹:
/Documents and Settings/[user]/Cookies
另一方面,对于Mozilla Firefox浏览器则将cookie保存在下面文件夹:
/Documents and Settings/[user]/Application Data/Mozilla/Firefox/Profiles/[random folder name]/Cookies.txt
应注意IE和Firefox浏览器都以明文形式存储cookie。所以不应该将诸如社会保险号,或信用卡号码之类的敏感信息存于cookie中。
1 cookie的安全性限制
为了避免电脑遭到袭击,浏览器对cookie有一些强制的安全显示。
首先,所有的cookie是域名独立的。163网易设置的cookie,yahoo网站访问不到。浏览器创建一个cookie时,会记录关联到cookie的域名,不会将其发送到另一个域名。
其次,浏览器存储cookie的重要限制是其大小的限制。一个域名存储的cookie总大小不能超过4096byte。这个大小限制,包含所有的cookie名称和值在内。
最后,大多数浏览器都限制可以被设置的cookie数量,一个域名不超过20个cookie(不包括IE浏览器)。超过这个数目,旧的cookie会被自动删除。
2 创建cookie
可以通过Response.Cookies集合添加cookie来创建新的cookie。Response.Cookies集合包含所有的Web服务器端发送到Web浏览器的cookie。
代码清单1所示的页面创建一个新的名叫Message的cookie。该页面包括一个输入Message这个cookie值的表单。
代码清单1:SetCookie.aspx
前台:SetCookie.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CookieDemo._Default" %>
<!DOCTYPE html PUBLIC "-//W 3C //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>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblCookieValue" Text="Cookie Value:" AssociatedControlID="txtCookieValue" runat="server" />
<asp:TextBox ID="txtCookieValue" runat="server" />
<asp:Button ID="btnAdd" Text="Add Value" runat="server" OnClick="btnAdd_Click" />
</div>
</form>
</body>
</html>
后台:SetCookie.aspx.cs
protected void btnAdd_Click(object sender, EventArgs e)
{
Response.Cookies["message"].Value = txtCookieValue.Text;
}
注意,cookie的名称是大小写敏感的。设置一个名叫message的cookie和设置一个名叫Message的cookie是不同的。
代码清单1所示的页面创建的是一个会话cookie(seesion cookie)。当关闭Web浏览器时,该cookie就消失了。如果希望创建持久化cookie(persistent cookie),则需要为cookie指定一个过期时间。
代码清单2所示的页面创建了一个持久化的cookie
代码清单2:SetPersistentCookie.aspx
前台:SetPersistentCookie.aspx代码
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="SetPersistentCookie.aspx.cs" Inherits="CookieDemo.SetPersistentCookie" %>
<!DOCTYPE html PUBLIC "-//W 3C //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>Set Persistent Cookie</title>
</head>
<body>
<form id="form1" runat="server">
<div>
You have visited this page
<asp:Label ID="lblCounter" runat="server" />
times!
</div>
</form>
</body>
</html>
后台:SetPersistentCookie.aspx.cs代码
protected void Page_Load(object sender, EventArgs e)
{
// Get current value of cookie
int counter = 0;
if (Request.Cookies["counter"] != null)
{
counter = Int32.Parse(Request.Cookies["counter"].Value);
}
// Increment counter
counter++;
// Add persistent cookie to browser
Response.Cookies["counter"].Value = counter.ToString();
Response.Cookies["counter"].Expires = DateTime.Now.AddYears(2);
// Display value of counter cookie
lblCounter.Text = counter.ToString();
}
代码清单2所示的页面记录页面被请求的次数。一个名为counter的持久化cookie被用来记录页面被请求的次数。注意,counter的过期时间被设为2年以后。当为某个cookie设置了过期时间后,cookie就被保存为持久化cookie了。
3 读取cookie
可以使用Response.Cookies集合创建和修改cookie,也可以使用Request.Cookies集合读取cookie的值。
代码清单3获取Request.Cookies集合中的所有cookie
ArrayList colCookie = new ArrayList();
for (int i = 0; i < Request.Cookies.Count; i++)
{
colCookie.Add(Request.Cookies[i]);
}
使用Request.Cookies集合时,非常重要的一点是,理解For…Each循环返回的值和For…Next循环返回的值的不同。如果用For…Each循环迭代Request.Cookies集合,返回的是cookies的名称。如果用For…Next循环迭代该集合,返回的是HttpCookie类的示例。
4 设置cookie属性
HttpCookie类代表cookie。当创建或读取一个cookie时,可以使用该类的下面这些属性。
n Domain——用于设置关联到cookie的域名,默认值是当前域名。
n Expires——用于通过给定一个过期时间创建一个持久化cookie。
n HasKeys——用于指定该cookie是否是一个多值cookie。
n HttpOnly——用于避免cookie被JavaScript访问。
n Name——用户指定cookie的名称。
n Path——用于指定关联到cookie的路径。默认值为/。
n Secure——用于指定cookie需要通过安全Socket层(SSL)连接传递。
n Value——允许读/写cookie的值。
n Values——当使用多值cookie时,用户读/写特定的值。
这些属性中的一部分需要更多的解释。例如,你可能发现Domain属性有些奇怪,因为你修改不了关联到cookie的domain。
Domain属性对于组织子域名时会非常有用。如果需要设置cookie可以被Sales.MyCompany.com,Managers.MyCompany.com和Support.MyCompany.com访问,则需要设置Domain属性值为.MyCompany.com(注意开头的部分),而不能使用该属性关联cookie到一个完全不同的域名。
HttpOnly属性用于设置一个cookie是否可以通过JavaScript访问。该属性只能对IE6(SP1)及以上版本有效。引入该属性是为了防止跨站点脚本攻击。
Path属性用于限定cookie到一个特定的路径。例如,如果在相同的域名部署多个应用程序,而不希望应用程序共享相同的cookie,则需要设置Path属性避免一个应用程序读取另一个应用程序的cookie。
Path属性听起来很有用。不幸的是,你不应该使用它。IE对于路径是大小写敏感的。如果用户在地址输入一个不同大小写的路径,则cookie不会被发送。换句话说,下面两个路径不匹配:
http://localhost/original/GetAllCookies.aspx
http://localhost/ORIGINAL/GetAllCookies.aspx
5 删除cookie
删除一个cookie的方法并不直观。要删除一个存在的cookie,必须设置其过期时间为一个过去的时间。
例如,代码清单4:
// 删除指定cookie
Response.Cookies["cookie名"].Expires = DateTime.Now.AddDays(-1);
代码清单5所示的是删除当前域名(和路径)下的所有cookie。
// 删除域名下的所有cookie
string[] cookies = Request.Cookies.AllKeys;
foreach (string cookie in cookies)
{
Response.Cookies[cookie].Expires = DateTime.Now.AddDays(-1);
}
代码清单5所示的从Request.Cookies集合中循环遍历并删除所有的cookie。
6 使用多值cookie
根据cookie规范,对单个域名,浏览器不能存储超过20个cookie。可以通过创建多值cookie来超越该限制。多值cookie是一个包含子键的单一cookie。可以根据需要创建任意数量的子键。
例如,代码清单6所示创建了一个名为preferences的多值cookie。Preferences cookie用于存储first name(名)、last name(姓)和favorite color(喜爱的色彩)
// 创建多值cookie
Response.Cookies["perferences"]["firstname"] = "God";
Response.Cookies["perferences"]["lastname"] = "Heaven";
Response.Cookies["perferences"]["favoriteColor"] = "White";
Response.Cookies["perferences"].Expires = DateTime.MaxValue;
当提交代码清单6所示页面时,下面的HTTP首部被发送到浏览器:
Set-Cookie:preferences=firstname=God&lastname=Heaven&favoriteColor=White;
expires=Fri,31-Dec-9999 23:59:59 GMT; path=/
代码清单7所示从preferences cookie读取值
// 读取多值cookie
if (Request.Cookies["perferences"] != null)
{
string strFirstName = Request.Cookies["perferences"]["firstname"];
string strLastName = Request.Cookies["perferences"]["lastname"];
string strFavoriteColor = Request.Cookies["perferences"]["favoriteColor"];
}
可以使用HttpCookie.HasKey属性判断一个cookie是一个普通cookie还是一个多值cookie。
下篇将介绍会话状态(Seesion)的状态维护。