在
ASP.NET
中使用
Cookie
的基本概念
摘要:
说明如何使用
c#
在
ASP.NET Web
应用程序中读取和写入
HTTP Cookie
。
适用于
·
ASP.NET
·
Microsoft® Visual Studio® .NET
·
Web
窗体
适合程度:
Web
程序开发初学者
目录
简介
何谓 Cookie?
Cookie 限制
撰写 Cookie
多重值 Cookie (子机码)
控制 Cookie 范围
读取 Cookie
修改和删除 Cookie
Cookie 和安全性
检查浏览器是否接受 Cookie
Cookie 和工作阶段状态
简介
Cookie
是在
Web
应用程序中提供储存使用者特定信息的有用方法。例如,当使用者造访网站时,您可以使用
Cookie
来储存使用者偏好设定或其它信息。当使用者下次造访您的网站时,应用程序可以撷取先前储存的信息。
本文提供在
ASP.NET
应用程序中使用
Cookie
的概观。我将说明在
ASP.NET
应用程序中使用
Cookie
的技巧,例如撰写
Cookie
并于稍后再读取。同时,我将说明
Cookie
的各种功能和特性,以及
ASP.NET
对
Cookie
的支援。
何谓
Cookie
?
Cookie
是伴随使用者要求和网页在
Web
服务器和浏览器之间传送的小段文字。
Cookie
包含
Web
应用程序在使用者造访网站时可读取的信息。
想象当使用者由网站
(www.contoso.com)
要求网页时,您的应用程序不只传送网页,还有包含日期和时间的
Cookie
。当使用者的浏览器取得网页时,浏览器也会取得储存在使用者硬盘中某数据夹内的
Cookie
。
稍后,使用者再次由网站要求网页。当使用者输入
URL www.contoso.com
时,浏览器会在本机硬盘中寻找该
URL
相关联的
Cookie
。如果有这个
Cookie
,浏览器就会将
Cookie
随着网页要求一起传送至您的网站。然后您的应用程序可决定该使用者上次造访网站的日期和时间。您可以使用此信息对使用者显示讯息、检查到期时间或执行任何其它有用的功能。
Cookie
与网站相关联,并非关联特定网页,因此浏览器和服务器会交换
www.contoso.com
的
Cookie
信息,不论使用者由网站要求哪个网页,浏览器和服务器都会交换
www.contoso.com
的
Cookie
信息。当使用者造访不同的网站时,每一个网站都可能传送
Cookie
给使用者的浏览器,浏览器会分别储存所有的
Cookie
。
这就是
Cookie
运作的基本方式。但是它们有何好处呢?基本上,
Cookie
可协助网站储存访客的相关信息。广义来说,
Cookie
是维持
Web
应用程序连续性的一种方法
(
正确来说是执行「状态管理」
)
。除了实际交换信息的短暂时间之外,浏览器和
Web
服务器并未联机。您对
Web
服务器所做的每一个要求都与其它要求分开处理。不过,许多时候在您要求网页时,让
Web
服务器能辨识您的身份非常有用。例如,购物网站的
Web
服务器可用来追踪各个购物者,让网站可以管理购物车和其它使用者特定信息。因此
Cookie
的作用类似电话卡,可显示适当的识别信息,以协助应用程序继续执行。
Cookie
有各种用途,其目的均在协助网站记住您的相关信息。例如,执行民意调查的网站可能只是使用
Cookie
作为布尔值,指出您的浏览器是否已经参与投票,让您不会重复投票。需要您登入的网站可能使用
Cookie
来告诉它您已经登入,因此您不必重复输入认证信息。
如需更多有关
Cookie
的背景信息,建议您参阅
http://www22.verizon.com/about/community/learningcenter/articles/displayarticle1/0,4065,1022z1,00.html Verizon
网站中的
How Internet Cookies Work (
英文
)
。作者说明了有关何谓
Cookie
及浏览器和服务器之间如何交换
Cookie
的详细信息。其中还包括关于
Cookie
涉及的隐私权问题摘要。
附带一提,您可曾想过它们为何叫做
Cookie
吗?
Jargon File (
又称为
The New Hacker's Dictionary)
版本
4.3.3
中有很好的定义,并对其来源提出合理的解释。您可以在
http://www.catb.org/~esr/jargon/jargon.html#cookie
中找到这个字。
从这里开始,我将假设您了解何谓
Cookie
,以及为何要在
ASP.NET
应用程序中使用
Cookie
。
Cookie
限制
在开始说明使用
Cookie
的技巧之前,我想先提出几点相关的限制。大多数的浏览器都支持可达
4096
个字节的
Cookie
。这个大小对于在使用者的计算机中储存一些值已经很够,但是请勿在
Cookie
中储存数据集或其它有可能很占空间的数据。更实际来说,不要在
Cookie
中储存大量的使用者信息。而是要储存使用者编号或其它识别码。然后,当使用者再度造访网站时,您就可以用使用者
ID
来查询数据库中使用者的详细数据
(
但是,有关储存使用者信息的详细信息,请参阅本文
Cookie 和安全性)
。
浏览器也会对可在使用者的计算机上储存的
Cookie
数量有所限制。大多数浏览器只允许每个网站储存
20
个
Cookie
,如果您尝试储存更多
Cookie
,则浏览器会舍弃最旧的
Cookie
。有些浏览器还会对
Cookie
数量加上绝对限制,通常可接受来自所有网站共
300
个
Cookie
。
您比较可能碰到的
Cookie
限制是使用者可设定其浏览器拒绝接受
Cookie
。您无法解决这个问题,除非完全避免使用
Cookie
,而使用不同的机制来储存使用者特定信息。储存使用者信息的常用方法是「工作阶段状态」,但工作阶段状态会依赖
Cookie
,稍后我会在
Cookie 和工作阶段状态
中说明。
注意
如需有关状态管理和在
Web
应用程序中储存信息的选项之详细信息,请参阅《
Introduction to Web Forms State Management
》
(
英文
)
和《
State Management
》
(
英文
)
。
原则上,虽然
Cookie
在应用程序中非常有用,但应用程序不应该依赖储存
Cookie
的功能。将
Cookie
用于附加功能,不要用来支持关键的重要功能。如果应用程序必须依赖
Cookie
,您可以测试浏览器是否接受
Cookie
。在本文稍后的
检查浏览器是否接受 Cookie
中将描述其做法。
撰写
Cookie
使用网页的
Response
属性来撰写
Cookie
,它会公开
(Expose)
一个对象,让您在浏览器要呈现的网页中加入新信息。
Response
对象支持称为
Cookies
的集合,可以在其中新增您要写入浏览器的
Cookie
。
注意
Response
对象和
Request
对象
(
我稍后会简单介绍
)
是网页的属性,分别包含
HttpResponse
和
HttpRequest
类别的执行个体。如需有关
Response
和
Request
相关文件的信息,请搜寻
HttpResponse
和
HttpRequest
。
在建立
Cookie
时,您要指定几个值。首先,指定
Cookie
名称和其中储存的值。您可以建立多个
Cookie
,每一个
Cookie
都必须有唯一的名称,让您稍后要读取时可加以识别
(Cookie
依名称来储存,因此如果建立具有相同名称的两个
Cookie
,就会覆写另一个
Cookie)
。
您可能也要指定
Cookie
的到期时间和时间。
Cookie
通常写入使用者的磁盘中,且可能会一直保存。因此您可以指定
Cookie
到期的日期和时间。当使用者再度造访网站时,浏览器会先检查网站的
Cookie
集合。如果
Cookie
已经到期,浏览器在传送网页要求给服务器时,就不会传送这个
Cookie
,而到期的
Cookie
会被删除
(
您的网站可能写入多个
Cookie
至使用者的计算机中,每一个
Cookie
可以各自有其到期日和时间
)
。请注意,浏览器负责管理硬盘上的
Cookie
,这会影响在应用程序中
Cookie
的用途,我稍后会说明。
Cookie
的到期期间应该多长?这要看您使用
Cookie
作何用途而定,换句话说,这要看您的应用程序认为
Cookie
值有效的时间有多长而定。如果您使用
Cookie
来计算造访网站的使用者人数,就可以依一年内都未造访网站则视为新访客的算法,将到期时间设定为现在起算一年。相对来说,如果使用
Cookie
来储存使用者偏好设定,就可以将到期时间设定为永久不会到期
(
例如,到期时间为
50
年
)
,因为使用者可能会认为定期重设偏好设定会很烦人。您可能有时候会撰写在数秒或数分钟内到期的
Cookie
。在本章稍后的
检查浏览器是否接受 Cookie
一节,我提供建立实际存留期以秒计算的
Cookie
范例。
注意
请记住使用者可以随时清除计算机中的
Cookie
。即使您储存很久才到期的
Cookie
,使用者也可以决定删除所有的
Cookie
,清掉您储存在
Cookie
中的任何设定。
如果不要设定
Cookie
的到期时间,也会建立
Cookie
,但并非储存在使用者的硬盘中。而此
Cookie
成为使用者工作阶段信息的一部份。当使用者关闭浏览器或工作阶段逾时时,就会舍弃该
Cookie
。像这种非永续性
Cookie
对于只需短暂储存的信息,或是基于安全考虑而不应写入客户端计算机磁盘时非常方便。例如,如果使用者在公用计算机上工作,此时不要将
Cookie
写入磁盘,则非永续性
Cookie
非常有用。
您可以用许多方法新增
Cookie
至
Response Cookies
集合中。下列范例显示完成此工作的两个方法。
方法一:
Response.Cookies["userName"].Value = "mike";
Response.Cookies["userName"].Expires = DateTime.Now.AddDays(1);
方法二:
System.Web.HttpCookie aCookie = new HttpCookie("userName ");
aCookie.Value = DateTime.Now.ToString();
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);
此范例新增两个
Cookie
至
Cookie
集合,一个称为「
userName
」,另一个称为「
lastVisit
」。对于第一个
Cookie
,我直接设定
Response.Cookies
集合的值。您可以用这种方式新增值至集合中,因为
Response.Cookies
是从型别
NameObjectCollectionBase
的特殊集合衍生。
对于第二个
Cookie
,我建立
Cookie
对象的执行个体
(
型别
HttpCookie)
,设定其属性,然后使用
Add
方法将它加入
Response.Cookies
集合中。在产生
HttpCookie
对象的执行个体时,您必须将
Cookie
名称当作建构函式的一部份来传递。
这两个范例都完成相同的工作,亦即将
Cookie
写入浏览器中。您使用的方法大部份视个人偏好而定。您可能发现第二种方法比较容易设定
Cookie
属性,但您也可以看出,其间的差异并不大。
在这两种方法中,到期时间的值必须为
DateTime
型别。不过,「
lastVisited
」值也是日期时间值。不过,在此状况下,我必须将日期时间值转换成字符串。您储存在
Cookie
的任何值最终都以字符串储存。
窥视您的
Cookie
您可能发现查看建立
Cookie
的效果非常有用。查看
Cookie
非常容易
—
毕竟它们是文字文件,只要您找得到它们就可以了。不同的浏览器以不同方式储存
Cookie
。我将说明
Internet Explore
如何储存
Cookie
,如果您使用不同的浏览器,请检查浏览器的
[
说明
]
以了解处理
Cookie
的方式。
检视
Cookie
的一种方便方式是让
Internet Explore
为您找出来。在
Internet Explore
中,在
[
工具
]
菜单选择
[Internet
选项
]
。在
[
一般
]
标签中,按一下
[
设定
]
,再按一下
[
检视档案
]
。
Internet Explore
会开启显示其所有暂存盘的窗口,包括
Cookie
。在此窗口中寻找名称以「
Cookie
」开头的档案,或寻找文字文件。连按两下
Cookie
,就会在预设的文字文件中将它开启。
或者也可以藉由寻找硬盘中的文字文件来浏览
Cookie
。
Internet Explore
在档案中储存网站的
Cookie
,这个文件名称的格式为
<user>@<domain>.txt
,其中
<user>
就是您的账户名称。例如,如果您的名称是
mikepope
,而您造访网站
www.contoso.com
,则该网站的
Cookie
将称为
mikepope@www.contoso.txt
(
此名称可能包含序号,例如
mikepope@www.contoso[1].txt
)
。
Cookie
文字文件是使用者特定的档案,因此可用账户来区隔。例如在
Windows XP
中,您将在如下列名称的目录中找到
Cookie
:
c:/Documents and Settings/<user>/Cookies
若要寻找您建立的
Cookie
,您会发现依
[
修改日期
]
将目录排序后再寻找最近的档案,会很有帮助。
您可以使用文字编辑器开启
Cookie
。如果档案包含多重
Cookie
,它们会以星号
(*)
隔开。每一个
Cookie
的第一行是它的名称,第二行是它的值。其余各行具有
Cookie
的管理信息,例如到期日和时间。在
Cookie
中还有一个简单的总和检查码,如果您变更
Cookie
名称或值的长度,浏览器将侦测到此一窜改而舍弃该
Cookie
。
多重值
Cookie (
子机码
)
先前的范例对您要储存的每个值使用一个
Cookie (
使用者名称、上次造访时间
)
。您也可以在单一
Cookie
中储存多重名称
-
值对。名称
-
值对是指「机码」或「子机码」,视您所读取的对象而定
(
子机码配置成类似
URL
中的查询字符串,您也许碰巧熟悉该结构
)
。例如,您可以建立称为「
userInfo
」的单一
Cookie
,其中具有子机码「
userName
」和「
lastVisit
」,以取代建立称为「
userName
」和「
lastVisit
」两个个别的
Cookie
。
有许多原因要使用子机码来取代个别的
Cookie
。很明显地,将相关或类似的信息放入单一
Cookie
中比较有条理。此外,因为所有的信息都在单一
Cookie
中,
Cookie
属性
(
例如到期时间
)
可套用至所有的信息中
(
反之,如果您要指定不同的到期时间给不同类型的信息,就应该将信息分别存放在个别的
Cookie
中
)
。
具有子机码的
Cookie
还能协助您维持
Cookie
的大小。如先前在
Cookie 限制
所提示的,
Cookie
上限为
4096
个字节,而且每一个网站不可以储存超过
20
个
Cookie
。藉由使用具有子机码的单一
Cookie
,可以使用少于指派给网站的
20
个
Cookie
。此外,单一
Cookie
会多占用大约
50
个字符
(
到期信息等
)
,加上储存在其中的数值长度,总共接近
4K
上限。如果以储存五个子机码来取代五个个别的
Cookie
,可以节省个别
Cookie
多用的空间,大约可节省
200
个字节。
若要建立具有子机码的
Cookie
,可以使用许多撰写单一
Cookie
的语法。下列范例显示撰写相同
Cookie
的两种方法,各具有两个子机码:
方法一
:
Response.Cookies["userInfo"]["userName"] = "mike";
Response.Cookies["userInfo"]["lastVisit"]= DateTime.Now.ToString();
Response.Cookies["userInfo"].Expires = DateTime.Now.AddDays(1);
方法二
:
System.Web.HttpCookie aCookie = new HttpCookie["userInfo "];
aCookie.Values["userName"]= "mike";
aCookie.Values["lastVisit"] = DateTime.Now.ToString();
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);
控制
Cookie
范围
在预设状况下,网站的所有
Cookie
都一起储存在客户端,而且会和任何网站要求一并传送至服务器
—
换句话说,网站中的每一个网页都可以取得该网站的所有
Cookie
。有时候您可能希望以特定方式使用
Cookie
,您可以用两种方式设定
Cookie
的范围:
·
将
Cookie
的范围限制在服务器上的数据夹,实际上可让您将
Cookie
限制到网站的应用程序。
·
将范围设定在网域,可让您指定网域中哪个子网域可以存取
Cookie
。
将
Cookie
限制在数据夹或应用程序
若要将
Cookie
限制在服务器上的数据夹,请设定
Cookie
的
Path
属性,如下所示:
System.Web.HttpCookie appCookie = new HttpCookie("AppCookie");
appCookie.Value = "written " + Now.ToString();
appCookie.Expires = Now.AddDays(1);
appCookie.Path = "/Application1";
Response.Cookies.Add(appCookie);
当然,您也可以直接设定
Response.Cookies
来撰写
Cookie
,如我先前所述。
路径可以为网站根目录或虚拟根目录之下的实体路径。只有在
Application1
数据夹或虚拟根目录之下的网页才可以使用
Cookie
。例如,如果网站称为
www.contoso.com
,先前范例所建立的
Cookie
就可供路径为
http://www.contoso.com/Application1/
的网页及该数据夹之下的任何网页使用。不过,此
Cookie
不可供其它应用程序中的网页使用,例如
http://www.contoso.com/Application2/
或
http://www.contoso.com/
。
秘诀
针对
Internet Explorer
和
Mozilla
浏览器进行的一些测试显示路径区分大小写。一般来说,
Windows
服务器上的
URL
并不区分大小写,但这似乎是例外。您不能控制使用者在浏览器中如何键入
URL
,但是如果您的应用程序依赖连结特定路径的
Cookie
,请确定您建立的任何超级链接中的
URL
都符合
Path
属性值的大小写。
限制
Cookie
范围为网域
在预设状况下,
Cookie
与特定网域相关联。例如,如果您的网站是
www.contoso.com
,使用者向该网站要求任何网页时,您写入的
Cookie
会传送至服务器
(
具有特定路径值的
Cookie
除外,如我在前一节中所述
)
。如果您的网站有子网域
—
例如
contoso.com
、
sales.contoso.com
和
support.contoso.com
—
则您可以关联
Cookie
与特定子网域。若要如此,请设定
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
使用。
读取
Cookie
当浏览器对服务器提出要求时,它会随着要求来传送该服务器的
Cookie
。在您的
ASP.NET
应用程序中,可以使用
Request
对象读取
Cookie
。
Request
对象的结构在本质上与
Response
对象的结构相同,因此从
Request
对象读取
Cookie
的方式类似于将
Cookie
写入
Response
对象的方式。下列范例显示取得称为「
username
」的
Cookie
值的两种方法,然后将其值显示在
Label
控件中:
if (Request.Cookies["userName"]!=null)
{
Label1.Text = Server.HtmlEncode(Request.Cookies["userName"].Value);
}
if (Request.Cookies["userName"] !=null)
{
System.Web.HttpCookie aCookie = new HttpCookie(Request.Cookies["userName"]);
Label1.Text = Server.HtmlEncode(aCookie.Value);
}
在尝试取得
Cookie
值之前,您应该确定该
Cookie
存在。否则,您将得到
System.NullReferenceException
例外状况。请注意,在将
Cookie
显示于网页之前,我呼叫
HttpServerUtility.HtmlEncode
方法将其内容编码。因为我要显示
Cookie
的内容
(
通常并不需要如此
)
才需要如此做,而且我要确定怀有恶意的使用者并未偷偷将可执行的指令码放入
Cookie
中。如需有关
Cookie
安全性的详细信息,请参阅
Cookie 和安全性
。
注意
因为不同的浏览器以不同方式储存
Cookie
,相同计算机上的不同浏览器未必可以相互读取另一个
Cookie
。例如,如果您先使用
Internet Explorer
来测试网页,稍后使用不同的浏览器再测试网页,则第二个浏览器找不到
Internet Explorer
所储存的
Cookie
。当然,多数人通常在所有的
Web
互动中使用相同的浏览器,因此在大多数状况下这并不是问题。不过,如果是测试应用程序的浏览器兼容性,就会有这个问题。
读取
Cookie
中子机码值的方式与设定其值的方式类似。下面是取得子机码值的一种方法:
if (Request.Cookies["userInfo"] !=null)
{
Label1.Text = Server.HtmlEncode(Request.Cookies["userInfo"]["userName"]);
Label2.text = Server.HtmlEncode(Request.Cookies["userInfo"]["lastVisit"]);
}
在先前的范例中,我取得子机码「
lastVisit
」的值,这是先前设定给
DateTime
字符串表示的值。请记住,
Cookie
以字符串来储存其值,因此如果您要使用
lastVisit
值作为日期,就必须将它转换成日期。
if (Request.Cookies["userInfo"] !=null)
{
System.Collections.Specialized.NameValueCollection UserInfoCookieCollection = new System.Collections.Specialized.NameValueCollection();
UserInfoCookieCollection = Request.Cookies["userInfo "].Values;
Label1.Text = Server.HtmlEncode(UserInfoCookieCollection["userName"]);
Label2.Text = Server.HtmlEncode(UserInfoCookieCollection["lastVisit "]);
}
和设定
Cookie
一样,用来读取
Cookie
的方法因人而异。
何谓到期日?
您可以读取
Cookie
的名称和值,但
Cookie
也没有多少其它值得再学习的了。您可以取得
Domain
和
Path
属性,但那些属性没有多大用处。例如,您可以读取
Domain
属性,但是如果您的网页与
Cookie
不在相同的网域中,本来就不会收到
Cookie
。
您无法读取的是
Cookie
的到期日和时间。当浏览器传送
Cookie
信息至服务器时,浏览器不包含到期信息。您可以读取
Expires
属性,但它传回的日期
-
时间值总是零。
稍早在
撰写 Cookie
中,我提到浏览器负责管理
Cookie
,
Expires
属性就是其中一个例子。
Expires
属性的主要目的是协助浏览器管理它所储存的
Cookie
。由服务器的观点来看,
Cookie
可以存在或不存在,到期日在服务器端并非有用的信息。因此,浏览器在传送
Cookie
时不提供这个信息。如果您在意
Cookie
的到期日,您必须将它重设,稍后我会在
修改和删除 Cookie
中说明。
更明确来说,在传送
Cookie
至浏览器之前,您可以读取
Cookie
中您已经在
Response
对象设定的
Expires
属性。不过,您无法由
Request
对象中取回到期日。
读取
Cookie
集合
先前的范例假设您要读取已知其名称的特定
Cookie
。您偶尔可能需要读取网页可用的所有
Cookie
。若要读取网页可用的所有
Cookie
的名称和值,您可以使用如下的程序代码来依序读取
Request.Cookies
集合:
string output = "";
System.Web.HttpCookie aCookie = new HttpCookie("info");
for(int i=0;i< Request.Cookies.Count;i++)
{
aCookie = Request.Cookies[i];
output += "Cookie
名称
= " +Server.HtmlEncode(aCookie.Name) + "<br>";
output += "Cookie
值
= " + Server.HtmlEncode(aCookie.Value) + "<br><br>";
}
Label1.Text = output;
注意
在执行此程序代码时,您可能会看到称为「
ASP.NET_SessionId
」的
Cookie
。那是
ASP.NET
用来储存您的工作阶段唯一识别码的
Cookie
。此工作阶段
Cookie
并不会一直存在您的硬盘中。如需有关工作阶段
Cookie
的详细信息,请参阅本文稍后的
Cookie 和工作阶段状态
。
先前范例的限制在于如果
Cookie
有子机码,显示画面会将子机码显示为单一名称
/
值字符串。
Cookie
属性
HasKeys
告诉您该
Cookie
是否有子机码。如果有,您可以展开至子机码集合,以取得个别子机码的名称和值。
如我先前所提示,您可以从
Cookie
属性
Values
取得子机码的相关信息,这是型别
NameValueCollection
的集合。您可藉由索引值直接从
Values
集合读取子机码值。对应的子机码名称可在
Values
集合的
AllKeys
成员中找到,它会传回字符串集合。
下列范例显示修改过的先前范例。此范例使用
HasKeys
属性来测试子机码,如果侦测到子机码,就会从
Values
集合取得子机码:
int
i ;
int
j;
string output = "";
System.Web.HttpCookie aCookie = new HttpCookie("info");
string subkeyName;
string subkeyValue;
for( i = 0 ;i<Request.Cookies.Count;i++)
{
aCookie = Request.Cookies[i];
output += "
名称 = " + aCookie.Name + "<br>";
if
(aCookie.HasKeys)
{
for(j = 0;j<aCookie.Values.Count;j++)
{
subkeyName = Server.HtmlEncode(aCookie.Values.AllKeys[j]);
subkeyValue = Server.HtmlEncode(aCookie.Values[j]);
output += "
子机码名称 = " + subkeyName + "<br>";
output += "
子机码值 = " + subkeyValue + "<br><br>";
}
}
else
{
output += "
值 = " + Server.HtmlEncode(aCookie.Value) + "<br><br>";
}
}
Label1.Text = output;
或者,您可以撷取子机码作为
NameValueCollection
对象,如下所示:
System.Web.HttpCookie aCookie = new HttpCookie("info");
string subkeyName;
string subkeyValue;
string output = "";
for(int i=0;i<Request.Cookies.Count;i++)
{
aCookie=Request.Cookies[i];
output += "
名称 = " + aCookie.Name + "<br>";
if(aCookie.HasKeys)
{
System.Collections.Specialized.NameValueCollection CookieValues = new 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 += "
子机码名称 = " +subkeyName + "<br>";
output += "
子机码值 = " +subkeyValue + "<br><br>";
}
}
else
{
output += "
值 = " + aCookie.Value + "<br><br>";
}
}
Label1.Text = output;
注意
请记住,我只呼叫
Server.HtmlEncode
方法,因为我要在网页中显示
Cookie
的值。如果您只要测试
Cookie
的值,在使用前就不必先加以编码。
修改和删除
Cookie
有时候您可能要修改
Cookie
,或许是变更其值或延长其到期时间
(
请记住,您无法读取到期日,因为浏览器并未传送到期日信息至服务器
)
。
当然,您实际上并非直接变更
Cookie
。虽然您可以从
Request.Cookies
集合取得
Cookie
并加以管理,但
Cookie
本身仍存在使用者硬盘中。因此修改
Cookie
事实上是以新的值建立新的
Cookie
,然后将此
Cookie
传送至浏览器来覆写客户端上的旧版本。
下列范例显示如何变更储存使用者造访网站次数的
Cookie
值:
int
counter;
if( Request.Cookies["counter"]==null)
{
counter = 0;
}
else
{
counter =Convert.ToInt32(Request.Cookies["counter"]);
}
counter += 1;
Response.Cookies["counter"] = counter.ToString();
Response.Cookies["counter"].Expires = DateTime.Now.AddDays(1);
Label1.Text = counter.ToString();
或者:
System.Web.HttpCookie ctrCookie=null;
int counter;
if(Request.Cookies["counter"] == null)
ctrCookie = new System.Web.HttpCookie("counter");
else
ctrCookie = Request.Cookies["counter"];
counter = Convert.ToInt32(ctrCookie.Value) + 1;
ctrCookie.Value = counter.ToString();
ctrCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(ctrCookie);
Label1.Text = Request.Cookies["counter"].Value.ToString();
删除
Cookie
删除
Cookie
—
实际将它从使用者的硬盘中移除
—
也是修改的一种。您不可以直接移除
Cookie
,因为此
Cookie
在使用者的计算机中。不过,您可以让浏览器为您删除
Cookie
。技巧在于以上述方式修改
Cookie (
也就是使用相同名称建立新的
Cookie)
,但是将
Cookie
的到期日设定为比今天还早。当浏览器检查
Cookie
到期日时,浏览器就会舍弃已经过时的
Cookie
。
因此,,删除任何一个
Cookie
与建立
Cookie
的方法相同,只是使用比今天还早的日期。下列范例比删除单一
Cookie
还有趣
—
其中显示删除目前网域中所有
Cookie
的一种方法:
System.Web.HttpCookie aCookie = new HttpCookie("DEL");
int limit = Request.Cookies.Count;
for(int i = 0;i<limit;i++)
{
aCookie = Request.Cookies[i];
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
的到期时间,因为那会移除整个
Cookie
而不是单一子机码。解决方法是管理保存子机码的
Cookie Values
集合。首先从
Request.Cookies
对象中取得
Cookie
以重新建立
Cookie
。然后呼叫
Values
集合的
Remove
方法,将要删除的子机码名称传送给
Remove
方法。然后和平常一样,您可以将修改过的
Cookie
加入
Response.Cookies
集合中,将修改完成的
Cookie
传回给浏览器。
下列程序代码显示如何删除子机码。在此范例中,以变量来指定要移除的子机码名称。
string subkeyName = "
userName ";
System.Web.HttpCookie aCookie = Request.Cookies["userInfo "];
aCookie.Values.Remove(subkeyName);
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);
Cookie
和安全性
在使用
Cookie
时,必须了解到它们所衍生的安全性漏洞。说到安全性,我指的不是稍早在
何谓 Cookie?
中说明的隐私权问题,隐私权比较像是关心
Cookie
信息使用方式的使用者问题。
Cookie
的安全性问题类似于从客户端取得数据的问题。首先,就应用程序来说,
Cookie
是另一种形式的使用者输入,因此可能会受到窥视和诈骗。使用者至少可看到储存在
Cookie
中的数据,因为
Cookie
就在使用者自己的计算机中。如果使用者需要,也可以在浏览器将
Cookie
传送给您之前加以变更。
结论是您绝不可在
Cookie
中储存机密—不可储存使用者名称、密码、信用卡号码等。请勿将不可让使用者或窃取
Cookie
的人得手的任何信息储存在
Cookie
中。
同理,对从
Cookie
取得的信息采保留态度。请勿认定数据与您写入时相同,使用
Cookie
值时,采取和处理使用者在网页中键入的数据相同的安全防护。例如,在网页显示数值之前,我在
Cookie
的内容上套用
HTML
编码。这是从使用者取得任何信息之后加以清理的标准方法,而处理
Cookie
也一样。
另一个问题是
Cookie
在浏览器和服务器之间以纯文字传送,可以拦截
Web
数据流量的任何人都可以读取
Cookie
。您可以设定
Cookie
属性,使其只在使用
Secure Sockets Layer (SSL
,又称为
https://)
联机时才传送
Cookie
。
SSL
并不能保护
Cookie
在使用者的计算机上免于遭到读取或处理,但是它可以防止
Cookie
在传输中遭到拦截。在本文中并不讨论
SSL
,但是要了解到您可以新增传输防护至
Cookie
中。如需有关
SSL
的详细信息,请参阅《
Secure Sockets Layer: Protect Your E-Commerce Web Site with SSL and Digital Certificates
》
(
英文
)
。
既然有这些安全性问题,您要如何安全地使用
Cookie
呢?您可以在
Cookie
中储存非关键性数据,例如使用者偏好设定或一些其它假使遭到破坏,对应用程序不会有重大影响的信息。如果您一定要在
Cookie
中储存机密信息,例如使用者
ID
,您可以将
Cookie
加密。一个可能是使用
[ASP.NET
窗体验证
]
公用程序来建立存成
Cookie
的验证票证。在本文中并不讨论加密,但如果您要在
Cookie
中储存机密数据,就应该深入了解如何隐藏信息,以免遭到窥视和诈骗。
检查浏览器是否接受
Cookie
稍早在
Cookie 限制
一节中曾提到使用者可能将其浏览器设定为拒绝接受
Cookie
的潜在问题。您如何知道是否可以写入和读取
Cookie
?如果无法写入
Cookie
,并不会产生错误
(
例如
Response.Cookies
不会掷出例外状况
)
,因为服务器不会追踪网页呈现之后所发生的事。浏览器同样也不会传送有关目前的
Cookie
设定的任何信息至服务器
(
更明白来说,
HttpBrowserCapabilities.Cookies Property
属性不会告诉您是否启用
Cookie
,它只会告诉您目前的浏览器是否原本就支持
Cookie)
。
决定是否接受
Cookie
的一种方法是尝试写入
Cookie
,然后再尝试读取。如果无法读取您写入的
Cookie
,就表示浏览器关闭
Cookie
功能。
我举出一个简单的范例来说明如何测试是否接受
Cookie
。此范例含有两个网页。在第一个网页中,我写入
Cookie
,然后将浏览器重新导向至第二个网页。第二个网页尝试读取
Cookie
。然后再将浏览器重新导向回到第一个网页,在
URL
中加入包含测试结果的查询字符串变量。
第一个网页的程序代码如下:
private void Page_Load(object sender, System.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
{
labelAcceptsCookies.Text = "
接受
cookies = " +Request.QueryString["AcceptsCookies"];
}
}
}
此网页先测试这是否为自动回传,如果不是,此网页会搜寻包含测试结果的查询字符串变量
(
AcceptsCookies
)
。如果没有查询字符串变量,表示测试尚未完成,因此程序代码会写出称为「
TestCookie
」的
Cookie
。在写出
Cookie
之后,此范例呼叫
Response.Redirect
传送至测试网页
(TestForCookies.aspx)
。附加至测试网页
URL
之后的是称为
redirect
的查询字符串变量,其中包含目前网页的
URL
,如此可让您在执行测试之后重新导向回到此网页。
此测试网页可包含整个程序代码,它不需要包含控件。下面是我使用的程序代码:
private void Page_Load(object sender, System.EventArgs e)
{
string redirect = Request.QueryString["redirect"];
int acceptsCookies;
//Cookie
是否被接受?
if(Request.Cookies["TestCookie"]==null)
{
acceptsCookies = 0; //
没有
cookie
,不能被接受
;
}
else
{
acceptsCookies = 1;
Response.Cookies["TestCookie"].Expires = DateTime.Now.AddDays(-1); //
删除测试用
cookie
}
Response.Redirect(redirect+"?AcceptsCookies=" + acceptsCookies,true);
}
在读取
redirect
查询字符串变量之后,此程序代码会尝试读取
Cookie
。基于管理目的,如果此
Cookie
确实存在,就会立即将它删除。完成测试时,此程序代码会使用
redirect
查询字符串变量中传回的
URL
来建构新的
URL
。新的
URL
还包含含有测试结果的查询字符串变量。最后一个步骤是使用新的
URL
将浏览器重新导向至原始网页。
此范例非常简单,但藉由尝试此程序并查看其发生的状况,可说明测试的基本原则。最大好处是将
Cookie
测试结果保存在永久存留的储存区,让使用者不用在每次检视原始网页时重复执行测试。不过,这比表面上看起来还难以处理。很明显地,
Cookie
无法使用。另一个可能是将测试结果储存在工作阶段状态中,但是在预设状况下,工作阶段状态也依赖
Cookie
,如果浏览器不接受
Cookie
,工作阶段状态也就无法运作。最后一个问题的解决方法是使用「无
Cookie
」工作阶段状态。在下一节中,将提供工作阶段状态如何处理
Cookie
的简介。
Cookie
和工作阶段状态
当使用者浏览网站时,服务器会为该使用者建立唯一的工作阶段,在使用者造访期间会持续存在。对于每一个工作阶段,
ASP.NET
都维持服务器式的结构
(
「工作阶段状态」
)
,应用程序可在此储存使用者特定信息。如需详细信息,请参阅《
Session State
》
(
英文
)
。
ASP.NET
需要可以追踪每一个使用者的工作阶段
ID
,因此可以将使用者对应至服务器上的工作阶段状态信息。在预设状况下,
ASP.NET
使用非永续性
Cookie
来储存工作阶段状态。如果使用
读取 Cookie
一节中的「读取
Cookie
集合」范例程序代码,您可能有看到工作阶段状态
Cookie
。
不过,如果使用者的浏览器停用
Cookie
,工作阶段状态就无法使用
Cookie
来储存工作阶段
ID
,且工作阶段状态无法使用。这就是我稍早在
检查浏览器是否接受 Cookie
一节中为何说测试
Cookie
之后,可能无法实际将测试结果存入工作阶段状态中的原因
—
没有
Cookie
就没有工作阶段状态。
ASP.NET
提供「无
Cookie
」工作阶段形式的暂时解决方法。您可以设定应用程序不在
Cookie
储存工作阶段
ID
,而是储存在网站网页的
URL
中。藉由将工作阶段
ID
保存在
URL
中,
ASP.NET
将
ID
储存在浏览器中,也就是说,可以在使用者要求另一个网页时将它取回。
无
Cookie
工作阶段可以避开浏览器拒绝
Cookie
的问题,让您使用工作阶段状态。如果您的应用程序依赖工作阶段状态,您可能要将它设定成使用无
Cookie
工作阶段。不过,在一些状况下,如果使用者与他人共享
URL
—
或许在使用者工作阶段使用中将
URL
以电子邮件传送给同事,则两个使用者可能会结束共享的相同工作阶段,而产生无法预期的结果。
如需设定应用程序使用无
Cookie
工作阶段的详细信息,请参阅
Knowledge Base
文章《
INFO: ASP.NET State Management Overview
》
(
英文
)
。
何谓 Cookie?
Cookie 限制
撰写 Cookie
多重值 Cookie (子机码)
控制 Cookie 范围
读取 Cookie
修改和删除 Cookie
Cookie 和安全性
检查浏览器是否接受 Cookie
Cookie 和工作阶段状态