ASP.NET Lab

The Best Web, The Best Future

博客园 首页 新随笔 订阅 管理

Cookies 为 Web 应用程序提供了存储特定用户信息的方法。例如,当一个用户访问网站的时候,你能够使用 Cookies 来存储该用户的偏好或其他信息。在用户下一次访问网站的时候,应用程序能够获取这些较早前被存储的信息。

什么是 Cookies?

Cookies 是随着请求连同页面一起在 Web 服务器和浏览器之间来回流动的小块文本。Web 应用程序能够随时在用户访问网站的时候读取 Cookies 中所包含的信息。

例如,如果用户在网站中请求页面的时候,应用程序除了返回一个页面之外,还返回了一个包含日期和时间的 Cookie,在用户的浏览器获得这个页面的时候,浏览器就会获得这个 Cookie,并把它存储到用户硬盘的特定目录中。

然后,如果用户想要再次从网站中请求该页面,当用户在浏览器中输入 URL 的时候浏览器就会在本地硬盘中查找与该 URL 相关联的 Cookie。如果 Cookie 存在,浏览器就会把它连同页面请求一起发送到网站中。这时候应用程序就能够检测到用户最后一次访问网站的日期和时间。你可能还会使用这个信息来为用户显示消息或者检查日期是否过期。

Cookies 是与网站相关联的,而不是与特定的页面相关联的,所以不论用户请求网站中的哪些页面,都将会在浏览器和服务器之间交换 Cookies 信息。与用户访问不同的网站一样,每个网站都能够把 Cookie 发送到用户的浏览器中;而浏览器会把所有来自于不同网站的 Cookies 分开进行存储。

Cookies 能够帮助网站存储访问者的相关信息。通常情况下,Cookies 是在 Web 应用程序中维护连续性的一种方式,也就是说,关于状态管理。除了在它们实际交换信息之外,浏览器和 Web 服务器之间连接通常都是断开的。用户访问 Web 服务器时所产生的每一个请求都被视为独立的。多数时候,只要用户在请求页面,它都能够帮助 Web 服务器对用户进行识别。例如,购物网站的 Web 服务器会始终保持对每个购物者的追踪,从而能够管理购物车和其他特定的用户信息。Cookie 因此扮演了名片的角色,呈现相关的鉴定信息来辅助应用程序的运行。

Cookies 能够用于多种用途,这些用途全部都涉及到帮助网站对于用户的记忆。例如,某个投票管理网站可能把 Cookie 简单地当成一个布尔值来指明用户的浏览器是否已经参与投票,从而禁止该用户的重复性投票;又如某个要求用户登入的网站会使用 Cookie 来记录用户是否已经登入,从而使得该用户不再需要重复输入验证信息。

Cookies 的局限性

大部分浏览器所支持的 Cookies 长度最多不能超过 4096 字节。因为这个限制,Cookies 最适合被用来存储小巧的数据,最好是标识符(如用户 ID)。用户 ID 这时候能够被用来识别当前用户并从数据库或其他数据存储中读取用户信息。(请参考本文中关于存储用户信息的安全含意信息的[Cookies 和安全]部分。)

浏览器同样限制了网站能够存储到用户浏览器中的 Cookies 数量。大部分浏览器只允许每网站最多只能存储 20 个 Cookies;如果你尝试存储更多,那么最旧的 Cookies 将被废弃。有些浏览器同样提出了一个绝对限制,它们所能接受的 Cookies 数量总和不能够超过 300(来自于所有网站的 Cookies 的总和)。

你可能遇到的另一个 Cookies 限制就是用户能够设置他们的浏览器来拒绝 Cookie。如果你定义了一个 P3P 机密策略并把它存放在网站的根目录,大部分浏览会都将接受来自网站中的 Cookies。但是,你可能已经完全避免了 Cookies 的使用,从而使用了另一个不同的机制来存储特定的用户信息。存储用户信息的一种通用的方式就是使用会话状态,但是会话状态又依赖于 Cookies,与本文中的[Cookies 和会话状态]部分所说明的一样。

提示:更多关于在 Web 应用程序中保存用户信息的状态管理和状态选项的信息,请参考:[ASP.NET 状态管理概览]和[ASP.NET 状态管理的建议]。

尽管 Cookies 在应用程序中是比较实用的,但是应用程序不应该对 Cookies 的使用而产生依赖。请不要使用 Cookies 来支持关键特征。如果你的应用程序必须依赖于 Cookies,你可以对浏览器进行测试并了解其是否接受 Cookies 的使用。请参考本文中的[检查浏览器是否接受 Cookies]部分。

写入 Cookies

浏览器负责在用户系统中管理 Cookies。发送到浏览器的 Cookies 经过 HttpResponse 对象暴露了一个名为 Cookies 的集合。你能够像访问 Page 类的 Response 属性一样来访问 HttpResponse 对象。任何你需要发送到浏览器的 Cookies 都必须被添加到这个集合中。在创建 Cookie 之后,你就需要为其指定 NameValue 属性。每个 Cookie 必须拥有唯一的名称才能够在浏览器中被读取的时候被识别。因为 Cookies 是通过名称来存储的,两个使用相同名称的 Cookies 中将会有一个会被覆盖。

你同样可以设置 Cookie 的过期日期和时间。已过期的 Cookies 会在用户访问网站并且重新写入 Cookies 的时候被浏览器所删除。Cookie 的过期可以根据应用程序所决定的 Cookie 有效期来进行设置。要使 Cookie 能够长期有效,你可以把过期日期设置成从现在所开始的后续 50 年时间。

提示:用户能够在任何时候清除计算机中的 Cookies。即使你已经存储了一个长期有效的 Cookie,用户还是能够自行决定来删除所有的 Cookies,这样一来,所有被存储在 Cookies 中的设置都将被消毁。

如果你没有设置 Cookie 的有效期,Cookie 仍然能够被创建,但是并不会被存储到用户的硬盘中。相反,这种 Cookie 是作为用户会话信息的一部分而被维护的。当用户关闭浏览器,Cookie 也随之被废弃。类似于这种非持续保持的 Cookie 在只需要短暂存储信息或者出于安全原因而不应该写入到客户端计算机的硬盘中的时候是比较有用的。例如,非持续保持的 Cookies 适用于用户在公共计算机上使用,或者你不想把 Cookie 写入到硬盘的情况下使用。

你能够使用多种方法把 Cookies 写入到 Cookies 集合中。如下实例说明了两个写入 Cookies 的方式:

Response.Cookies["userName"].Value = "patrick";
Response.Cookies["userName"].Expires = DateTime.Now.AddDays(1);

HttpCookie aCookie = new HttpCookie("lastVisit");
aCookie.Value = DateTime.Now.ToString();
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);

该实例在 Cookies 集合中添加了两个 Cookies,一个被命名为 userName 而另一个被命名为 lastVisit。至于第一个 Cookie,直接设置了 Cookies 集合的值。你可以使用这个方法来在集合中添加值,因为 Cookies 是继承自 NameObjectCollectionBase 类型的一个专用集合。

至于第二个 Cookie,代码创建了 HttpCookie 类型的一个对象实例,并设置它的属性,然后通过 Add 方法把它添加到 Cookies 集合中。当你实例化一个 HttpCookie 对象的时候,你必须把 Cookie 的名称作为构造器的一个部分进行传递。

这两个实例都完成了把 Cookie 写入到浏览器的相同任务。在这两个方法中,有效期属性值的类型必须是 DateTime。但是,lastVisited 中的值同样也是一个表示时间日期的值。因为所有 Cookie 的值都应该被存储成字符串格式,所以日期时间值会被自动转换成字符串。

拥有多个值的 Cookies

你能够在 Cookie 中存储单独的值,如用户名称或最后访问的时间。你同样可以在单独的 Cookie 中存储多个 Name/Value 数据对。Name/Value 数据对被引用成子键。(子键展开之后的格式与 URL 中的查询字符串相似。)例如,作为创建名为 userNamelastVisit 的两个分开的 Cookies 的替代,你可以创建名为 userInfo 的单独 Cookie 来包含子键 userNamelastVisit

你可能出于几个原因而使用子键。首先,它对于把相关信息或类似信息放进单独的 Cookie 中是比较方便的。另外,因为所有信息都在单独的 Cookie 中,Cookie 的参数(如有效期)会被应用到所有信息。(相反,如果你需要为不同类型的信息指定不同的有效期,那么你就应该把这些信息存储到单独的 Cookies 中。)

使用子键的 Cookie 同样有助于你突破 Cookie 文件尺寸的限制。与先前在[Cookie 的局限性]部分中所描述的一样,Cookies 的尺寸上限通常是 4096 个字节,并且你不能为单个网站存储超过 20 个以上的 Cookies。通过使用包含子键的单独 Cookie,你可以减少 Cookies 的使用数量。另外,单独的 Cookie 中最多只能出现 50 个字符(有效期信息,等等),加上保存在其中的值的长度,全部总和相加后接近 4096 个字节。如果你使用 5 个子键来取代 5 个分离的 Cookies,那么你能够在每个 Cookies 中保存接近 200 个字节的数据。

要创建包含子键的 Cookie,你就要使用不同的语法来写入单独的 Cookie。下列实例说明了写入相同 Cookie (分别包含了两个子键)的两个方式:

Response.Cookies["userInfo"]["userName"] = "patrick";
Response.Cookies["userInfo"]["lastVisit"] = DateTime.Now.ToString();
Response.Cookies["userInfo"].Expires = DateTime.Now.AddDays(1);

HttpCookie aCookie = new HttpCookie("userInfo");
aCookie.Values["userName"] = "patrick";
aCookie.Values["lastVisit"] = DateTime.Now.ToString();
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);

控制 Cookie 的作用范围

默认时,网站的所有 Cookies 都一起被存储在客户端,并且所有 Cookies 连同网站的任何请求一起被发送到服务器。换句话说,网站中的每个页面都能够为网站获取所有的 Cookies。但是,你能够通过两个方式来设置 Cookies 的作用范围:

  • 把 Cookies 的作用范围限制到服务器的目录,从而允许你把 Cookies 限制到网站的某个应用程序。

  • 设置域的作用范围,能够允许你指定域中的哪些子域能够访问 Cookie。

把 Cookies 限制到目录或应用程序

要把 Cookies 限制到服务器的目录,请设置 Cookie 的 Path 属性,如下实例所示:

HttpCookie appCookie = new HttpCookie("AppCookie");
appCookie.Value = "written " + DateTime.Now.ToString();
appCookie.Expires = DateTime.Now.AddDays(1);
appCookie.Path = "/Application1";
Response.Cookies.Add(appCookie);

提示:你同样可以通过直接添加到 Cookies 集合的方式来写入 Cookies,如前面的实例所示。

路径既能够位于网站的物理根目录中也能够位于虚拟根目录中。结果将会是 Cookie 只对于目录或者虚拟根 Application1 中的页面可用。例如,如果你的网站名称是 www.contoso.com,在前面的实例中所创建的 Cookie 将只对于路径 http://www.contoso.com/Application1/ 以及其下所有子目录中的页面可用。但是,该 Cookie 对于其他应用程序(如 http://www.contoso.com/Application2/ 或者 http://www.contoso.com/)中的页面则是不可用的。

提示:部分浏览器中的路径对大小写字母是敏感的。你无法控制用户如何在他们的浏览器中输入 URL,但是如果你的应用程序依赖于 Cookies 来约束特定的路径,请确保你所创建的任何超级链接中的 URL 能够与 Path 属性值中的大小写相匹配。

限制 Cookie 的域范围

默认时,Cookies 是与特定的域相关联的。例如,如果你的网站是 www.contoso.com,你写入的 Cookies 会在用户从网站中请求任何页面的时候被发送到服务器。(可能不包括有特定的路径值的 Cookies。)如果你的网站中还拥有子域(例如,contoso.com、sales.contoso.com、以及 support.contoso.com),那么你可以把特定的子域与 Cookies 相关联。要这样做,请设置 Cookie 的 Domain 属性,如实例所示:

Response.Cookies["domain"].Value = DateTime.Now.ToString();
Response.Cookies["domain"].Expires = DateTime.Now.AddDays(1);
Response.Cookies["domain"].Domain = "support.contoso.com";

当按照这样设置域之后,Cookie 将只对于特定域中的页面可用。你同样能够使用 Domain 属性来创建一个能够在多个子域之间被共享的 Cookie,如下实例所示:

Response.Cookies["domain"].Value = DateTime.Now.ToString();
Response.Cookies["domain"].Expires = DateTime.Now.AddDays(1);
Response.Cookies["domain"].Domain = "contoso.com";

Cookie 这时候将与 sales.contoso.com 和 support.contoso.com 域一样对于主域是可用的。

读取 Cookies

在浏览器向服务器发送请求的时候,它会把 Cookies 连同请求一起发送到服务器。在你的 ASP.NET 应用程序中,你能够使用 HttpRequest 对象来读取 Cookies,该对象是作为 Page 类的 Request 属性出现的。HttpRequest 对象的结构实质上与 HttpResponse 对象一样,所以你能够使用把 Cookies 写入到 HttpResponse 对象类似的方式从 HttpRequest 对象中读出 Cookies。如下代码实例说明了获取 Cookie (名为 userName)的值并显示到一个 Label 控件中的两种方式:

if(Request.Cookies["userName"] != null)
    Label1.Text = Server.HtmlEncode(Request.Cookies["userName"].Value);

if(Request.Cookies["userName"] != null)
{
    HttpCookie aCookie = Request.Cookies["userName"];
    Label1.Text = Server.HtmlEncode(aCookie.Value);
}

在尝试获取 Cookie 的值之前,你应该先确定该 Cookie 已经存在;如果该 Cookie 并不存在,你将会得到一个 NullReferenceException 异常。注意到在显示 Cookie 的内容之前已经调用了 HtmlEncode 方法进行编码。这使得恶意用户无法在 Cookie 中添加可执行脚本。关于 Cookie 安全性的更多信息,请参考:[Cookies 和安全]。

提示:因为不同的浏览器存储 Cookies 的方式都各不相同,相同计算机中的浏览器无法读取其他类型浏览器所设置的 Cookies。例如,如果你使用 Internet Explorer 来测试页面,然后又使用另一个不同的浏览器再次进行测试,第二个浏览器将无法找到由 Internet Explorer 所保存的 Cookies。

从 Cookie 中读取子键的值与设置的时候相似。如下代码实例说明了获取子键的值的一种方式:

if(Request.Cookies["userInfo"] != null)
{
    Label1.Text = 
        Server.HtmlEncode(Request.Cookies["userInfo"]["userName"]);

    Label2.Text =
        Server.HtmlEncode(Request.Cookies["userInfo"]["lastVisit"]);
}

在前面的实例中,代码读取子键 lastVisit 中的值,该值在前面的实例中被设置成一个被呈现成字符串的 DateTime 值。Cookies 把值存储成为字符串,所以如果你需要把 lastVisit 的值当成日期对象来使用,你就应该把它转换成适当的类型,如下实例所示:

DateTime dt;
dt = DateTime.Parse(Request.Cookies["userInfo"]["lastVisit"]);

Cookie 中的子键被类型化成为 NameValueCollection 类型的一个集合。因此,获取单独子键的另一种方式就是先获取子键集合然后通过名称来提取子键的值,如下实例所示:

if(Request.Cookies["userInfo"] != null)
{
    System.Collections.Specialized.NameValueCollection
        UserInfoCookieCollection;
       
    UserInfoCookieCollection = Request.Cookies["userInfo"].Values;
    Label1.Text = 
        Server.HtmlEncode(UserInfoCookieCollection["userName"]);
    Label2.Text =
        Server.HtmlEncode(UserInfoCookieCollection["lastVisit"]);
}
改变 Cookie 的过期时间

浏览器负责管理 Cookies,并且 Cookie 的过期时间和日期有助于浏览器对所存储的 Cookies 进行管理。因此,虽然你能够读取 Cookie 的名称和值,但是你无法读取 Cookie 的过期日期和时间。在浏览器把 Cookie 信息发送到服务器的时候,浏览器中并不包括有效期信息。(Cookie 的 Expires 属性始终返回为零的日期值。)如果你关心 Cookie 的过期时间,你就应该把它重置,具体操作被包含在本文的[更改并删除 Cookies]部分。

提示:在 Cookie 被发送到浏览器之前,你能够读取 Cookie 的 Expires 属性(该属性在 HttpResponse 对象中被设置)。但是,你不能从 HttpRequest 对象中反向获取过期时间。

读取 Cookie 集合

你偶尔可能需要读取所有的 Cookies 并为页面可用。要把所有 Cookies 的名称和值读取到页面中,你可以如下代码对 Cookies 集合进行循环。

System.Text.StringBuilder output = new System.Text.StringBuilder();
HttpCookie aCookie;
for(int i=0; i<Request.Cookies.Count; i++)
{
    aCookie = Request.Cookies[i];
    output.Append("Cookie name = " + Server.HtmlEncode(aCookie.Name) 
        + "<br />");
    output.Append("Cookie value = " + Server.HtmlEncode(aCookie.Value)
        + "<br /><br />");
}
Label1.Text = output.ToString();

提示:当你运行这段代码的时候,你可能会看到一个名为 ASP.NET_SessionId 的 Cookie。也就是 ASP.NET 用来为你的会话而存储唯一标识符的 Cookie。这个会话 Cookie 不会被保持到你的硬盘中。更多关于会话 Cookies 的信息,请参考本文中的[Cookies 和会话状态]部分。

前面实例的一个局限性就是如果 Cookie 拥有子键,执行结果会把子键显示成一个 Name/Value 字符串。你能够读取 Cookie 的 HasKeys 属性来检测 Cookie 中是否含有子键。如果是,你就能够读取子键的集合并获取单独的子键名称和值。你能够从 Values 集合中直接使用索引值来读取子键的值。相应的子键名称在 Values 集合的 AllKeys 成员中是可用的,从而返回一个字符串数组。你同样可以使用 Values 集合中的 Keys 成员。但是,AllKeys 属性在第一次被访问的时候就会被缓存起来。恰好相反,Keys 属性则在每次被访问的时候都建立一个新的数组。正是因为这个原因,AllKeys 属性在相同页面请求环境的后继访问中是非常高效的。

如下实例说明了对前面实例的修正。它使用 HasKeys 属性对子键进行测试,如果子键被检测到,那么该实例就从 Values 集合中获取子键:

for(int i=0; i<Request.Cookies.Count; i++)
{
    aCookie = Request.Cookies[i];
    output.Append("Name = " + aCookie.Name + "<br />");
    if(aCookie.HasKeys)
    {
        for(int j=0; j<aCookie.Values.Count; j++)
        {
            subkeyName = Server.HtmlEncode(aCookie.Values.AllKeys[j]);
            subkeyValue = Server.HtmlEncode(aCookie.Values[j]);
            output.Append("Subkey name = " + subkeyName + "<br />");
            output.Append("Subkey value = " + subkeyValue + 
                "<br /><br />");
        }
    }
    else
    {
        output.Append("Value = " + Server.HtmlEncode(aCookie.Value) +
            "<br /><br />");
    }
}
Label1.Text = output.ToString();

或者,你也可以像如下实例一样把子键当成 NameValueCollection 对象来提取:

System.Text.StringBuilder output = new System.Text.StringBuilder();
HttpCookie aCookie;
string subkeyName;
string subkeyValue;

for (int i = 0; i < Request.Cookies.Count; i++)
{
    aCookie = Request.Cookies[i];
    output.Append("Name = " + aCookie.Name + "<br />");
    if (aCookie.HasKeys)
    {
        System.Collections.Specialized.NameValueCollection CookieValues = 
            aCookie.Values;
        string[] CookieValueNames = CookieValues.AllKeys;
        for (int j = 0; j < CookieValues.Count; j++)
        {
            subkeyName = Server.HtmlEncode(CookieValueNames[j]);
            subkeyValue = Server.HtmlEncode(CookieValues[j]);
            output.Append("Subkey name = " + subkeyName + "<br />");
            output.Append("Subkey value = " + subkeyValue + 
                "<br /><br />");
        }
    }
    else
    {
        output.Append("Value = " + Server.HtmlEncode(aCookie.Value) +
            "<br /><br />");
    }
}
Label1.Text = output.ToString();

更改并删除 Cookies

你不能直接更改 Cookie。相反,你可以新建一个拥有新值的 Cookie 然后把它发送到浏览器以覆盖掉客户端中的旧版本。如下代码实例说明了你应该如何改变一个存储用户访问计数的 Cookie 值:

int counter;
if (Request.Cookies["counter"] == null)
    counter = 0;
else
{
    counter = int.Parse(Request.Cookies["counter"].Value);
}
counter++;

Response.Cookies["counter"].Value = counter.ToString();
Response.Cookies["counter"].Expires = DateTime.Now.AddDays(1);
删除 Cookies

删除 Cookie 的操作会从用户的硬盘中把 Cookie 文件删除,与更改操作有所不同。因为 Cookie 位于用户的计算机中,所以你不能够直接对它进行删除。但是,你能够借助于浏览器来删除 Cookie。方法就是创建一个与需要被删除的 Cookie 名称相同的新 Cookie,然后把新 Cookie 的有效期设置为早于当天的日期。一旦浏览器检查到新 Cookie 已经过期,就会自动把这些过期的 Cookies 进行废弃。如下代码实例说明了删除应用程序中所有 Cookies 变量的一种方式:

HttpCookie aCookie;
string cookieName;
int limit = Request.Cookies.Count;
for (int i=0; i<limit; i++)
{
    cookieName = Request.Cookies[i].Name;
    aCookie = new HttpCookie(cookieName);
    aCookie.Expires = DateTime.Now.AddDays(-1);
    Response.Cookies.Add(aCookie);
}
更改或删除子键

更改一个单独子键的方式与创建时一样,如下实例所示:

Response.Cookies["userInfo"]["lastVisit"] = DateTime.Now.ToString();
Response.Cookies["userInfo"].Expires = DateTime.Now.AddDays(1);

要删除一个单独的子键,你需要操作 Cookie 中保持子键的 Values 集合。你首先需要从 Cookies 对象中获取该 Cookie。然后调用 Values 集合的 Remove 方法,把要删除的子键名称传递给 Remove 方法。然后再把这个 Cookie 重新添加到 Cookies 集合中,于是被更改的 Cookie 再一次回到了浏览器。如下代码实例说明了如何删除一个子键。在实例中,要删除的子键名称在一个变量中被指定。

string subkeyName;
subkeyName = "userName";
HttpCookie aCookie = Request.Cookies["userInfo"];
aCookie.Values.Remove(subkeyName);
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);

Cookies 和安全

使用 Cookies 的安全问题与从客户端获取数据时的安全问题相似。在你的应用程序中,Cookies 是另一种类型的用户输入窗体,因此容易被恶意分析并进行欺骗。一旦 Cookie 在用户自己的计算机中变得可用,那么用户就能够查看到你存储在 Cookie 中的数据。而且 Cookie 中的内容在发送到服务器之前能够被用户进行更改。

你应该不要把机密数据(如用户名称、密码、信用卡号码、等等)存储到 Cookie 中。更加不要把任何能够用来窃取 Cookie 的信息保存到 Cookies 中。

同样,请始终保持对 Cookie 信息的警惕性。不要假设 Cookie 中的数据始终与被写入的时候保持一致;使用与处理 Web 页面中的用户输入数据相同的安全机制来处理 Cookie 的值。本文中较早部分的实例说明了在 Cookie 的值被显示到页面之前应该进行 HTML 编码,这与在显示任何用户信息之前需要对信息进行 HTML 编码是一样的。

Cookies 是以纯文本的格式在浏览器和服务器之间被传送的,并且能够对你的 Web 通讯进行截取的任何人都能够读取 Cookie 的内容。你可以设置一个 Cookie 属性来促使 Cookie 只被传送到使用了安全套接字层(SSL)的连接中。但是 SSL 并不对读取发生之前的 Cookie 或在用户计算机中被处理的 Cookie 进行保护,它只会在读取的过程中对 Cookie 的内容进行加密,从而保证不会被随意破译。更多信息,请参考[Web 应用程序的基本安全练习]。

检测浏览器是否接受 Cookies

用户能够设置他们的浏览器来拒绝 Cookies。但是 Cookie 无法被写入的时候并不会引发任何错误。浏览器同样也不会关于 Cookie 设定的信息发送到服务器。

提示:Cookies 属性也无法指明浏览器是否已经启用了 Cookies。它只会指明当前的浏览器的 Cookies 支持能力。

检测浏览器是否接受 Cookies 的一种方式就是尝试写入一个测试 Cookie 并尝试重新读取它的值。如果你不能够读取被你所写入的 Cookie,那么就可以确定该浏览器已经禁用了 Cookies。

如下代码实例说明了你应该如何测试浏览器是否接受 Cookies。该实例由两个页面所组成。第一个页面写出了一个 Cookie,然后浏览器被重定向到第二个页面。第二个页面尝试读取这个 Cookie。并再次重定向浏览器以返回到第一个页面,同时为 URL 添加一个包含测试结果的查询字符串。

第一个页面的代码看起来像这样:

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        if (Request.QueryString["AcceptsCookies"] == null)
        {
            Response.Cookies["TestCookie"].Value = "ok";
            Response.Cookies["TestCookie"].Expires =
                DateTime.Now.AddMinutes(1);
            Response.Redirect("TestForCookies.aspx?redirect=" +
                Server.UrlEncode(Request.Url.ToString()));
        }
        else
        {
            Label1.Text = "Accept cookies = " +
                Server.UrlEncode(
                Request.QueryString["AcceptsCookies"]);
        }
    }
}

这个页面首先测试自己是否已经被回传,如果没有,页面会在查询字符串中查看包含测试结果的变量 AcceptsCookies。如果查询字符串还不可用,则说明测试还没有完成,所以代码写出了一个名为 TestCookie 的 Cookie。在 Cookie 被写出之后,实例就调用 Redirect 方法转移到测试页面(TestForCookies.aspx)。并为测试页面的 URL 添加一个包含当前页面 URL 信息的查询字符串变量 redirect;这将允许你在完成测试之后通过重定向返回到该页面中来。

测试页面能够包含所有的代码;它不需要包含任何控件。测试页面中的内容如下代码实例所示。

protected void Page_Load(object sender, EventArgs e)
{
    string redirect = Request.QueryString["redirect"];
    string acceptsCookies;
    if(Request.Cookies["TestCookie"] ==null)
        acceptsCookies = "no";
    else
    {
        acceptsCookies = "yes";
        // 删除测试 Cookie。
        Response.Cookies["TestCookie"].Expires = 
            DateTime.Now.AddDays(-1);
    }
    Response.Redirect(redirect + "?AcceptsCookies=" + acceptsCookies,
    true);
}

在读取 redirect 查询字符串变量之后,代码就会尝试对 Cookie 进行读取。出于常规目的,如果 Cookie 存在,它会会立即被删除。一旦测试已经完成,代码就会从 redirect 查询字符串变量中被传递过来的 URL 中构造一个新的 URL。新的 URL 中同样有一个包含测试结果的查询字符串变量。最终的步骤就是使用这个新的 URL 把浏览器重定向到最初的页面。

该实例的一个重要改进就是把 Cookie 的测试结果保持到一个持续保持的存储中(如数据库),从而在测试的过程中不再需要每次都把用户重定向到最初的页面。(通过默认的 Cookies 需求把测试结果存储到会话状态中。)

Cookies 和会话状态

在用户导航到网站的时候,服务器会为该用户建立一个唯一的会话并维持着该用户的访问期限。至于每个会话,ASP.NET 都在应用程序能够存储特定用户信息的前提下对会话状态信息进行维护。更多信息,请参考:[会话状态概览]。

ASP.NET 必须追踪每个用户的会话 ID,从而能够把用户映射到服务器中的会话状态信息中。默认时,ASP.NET 使用一个非持续保持的 Cookie 来存储会话状态。但是,如果用户在浏览器中禁用了 Cookies,那么会话状态将无法通知也不能够被存储到 Cookie 中。

作为选择,ASP.NET 在窗体中提供了另外一种无 Cookie 的会话。你可以配置你的应用程序不把会话 ID 存储到 Cookie 中,而是存储到网站页面的 URL 中。如果你的应用程序依赖于使用会话状态,你可能需要考虑对它进行配置以使用无 Cookie 的会话。但是,在一些有限的环境中,如果用户可能会在自己的会话仍然处于活动期的情况下把 URL 共享给了其他人(如把 URL 发送给某个同事),那么,这两个用户都能够终止相同会话的共享,并且导致出现不可预知的结果。更多关于配置应用程序来使用无 Cookie 会话的信息,请参考:[ASP.NET 状态管理概览]。

posted on 2007-01-12 22:59  Laeb  阅读(2296)  评论(0编辑  收藏  举报