Cookies 想必所有人都了解, 但是未必所有人都精通。本文讲解了 Cookies 的各方面知识, 并且提出来了最佳实践。这是笔者在日常工作中的积累和沉淀。
Cookie 是一小段文本信息,伴随着用户请求和页面在 Web 服务器和浏览器之间传递。Cookie 包含每次用户访问站点时 Web 应用程序都可以读取的信息。
例如,如果在用户请求站点中的页面时应用程序发送给该用户的不仅仅是一个页面,还有一个包含日期和时间的 Cookie,用户的浏览器在获得页面的同时还获得了该 Cookie,并将它存储在用户硬盘上的某个文件夹中。
以后,如果该用户再次请求您站点中的页面,当该用户输入 URL 时,浏览器便会在本地硬盘上查找与该 URL 关联的 Cookie。如果该 Cookie 存在,浏览器便将该 Cookie 与页请求一起发送到您的站点。然后,应用程序便可以确定该用户上次访问站点的日期和时间。您可以使用这些信息向用户显示一条消息,也可以检查到期日期。
Cookie 与网站关联,而不是与特定的页面关联。因此,无论用户请求站点中的哪一个页面,浏览器和服务器都将交换 Cookie 信息。用户访问不同站点时,各个站点都可能会向用户的浏览器发送一个 Cookie;浏览器会分别存储所有 Cookie。
Cookie 帮助网站存储有关访问者的信息。一般来说,Cookie 是一种保持 Web 应用程序连续性(即执行状态管理)的方法。除短暂的实际交换信息的时间外,浏览器和 Web 服务器间都是断开连接的。对于用户向 Web 服务器发出的每个请求,Web 服务器都会单独处理。但是在很多情况下,Web 服务器在用户请求页时识别出用户会十分有用。例如,购物站点上的 Web 服务器跟踪每位购物者,这样站点就可以管理购物车和其他的用户特定信息。因此,Cookie 可以作为一种名片,提供相关的标识信息帮助应用程序确定如何继续执行。
使用 Cookie 能够达到多种目的,所有这些目的都是为了帮助网站记住用户。例如,一个实施民意测验的站点可以简单地将 Cookie 作为一个 Boolean 值,用它来指示用户的浏览器是否已参与了投票,这样用户便无法进行第二次投票。要求用户登录的站点则可以通过 Cookie 来记录用户已经登录,这样用户就不必每次都输入凭据。
Cookies 保存在用户的本地机器上,不同的浏览器存储在不同的文件夹中,并且按照域名分别保存。即网站之间的 Cookies 不会彼此覆盖。
IE 浏览器的用户可以通过在本地的文档中找到 Cookies 的 txt 文件, 不同操作系统的位置不同,windows server 2003/xp 都保存在:
C:\Documents and Settings\Administrator\Cookies 文件夹下。
其中名称 txt 按照域名保存,比如 localhost 域下的 cookies 为:
或者
其中后面的 [1] 和[2]是随着每次保存交替变化的。
Cookies 的信息是在 Web 服务器和浏览器之间传递的。保存在 Http 请求中。
在请求一个页面的 Http 头中,会将属于此页面的本地 Cookies 信息加在 Http 头中,注意下面加粗的部分:
- GET / Cookies / Test.aspx HTTP / 1.1 Host: localhost: 1335 User - Agent: Mozilla / 5.0(Windows; U; Windows NT 5.2; zh - CN; rv: 1.9.1.1) Gecko / 20090715 Firefox / 3.5.1 GTB5(.NET CLR 3.5.30729) Accept: text / html,
- application / xhtml + xml,
- application / xml;
- q = 0.9,
- *
- /*;q=0.8
- Accept-Language: zh-cn,zh;q=0.5
- Accept-Encoding: gzip,deflate
- Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
- Keep-Alive: 300
- Connection: keep-alive
- Cookie: My.Common.TestCookieInfo=Pkid=999&TestValue=aaabbbcccdddeee */
如果页面要求写入 Cookies 信息,则返回的 Http 如下,注意加粗的部分:
- HTTP/1.x 200 OK
- Server: ASP.NET Development Server/9.0.0.0
- Date: Thu, 06 Aug 2009 03:40:59 GMT
- X-AspNet-Version: 2.0.50727
- Set-Cookie: My.Common.TestCookieInfo=Pkid=999&TestValue=aaabbbcccdddeee; expires=Fri, 07-Aug-2009 03:40:59 GMT; path=/
- Cache-Control: private
- Content-Type: text/html; charset=utf-8
- Content-Length: 558
- Connection: Close
IE 用户可以直接查看 Cookies 的 txt 文件。
比如:C:\Documents and Settings\Administrator\Cookies\administrator@localhost[1].txt
FF 下使用 Web Developer 插件可以很方便的查看、删除和修改 Cookies:
插件截图:
查看页面 Cookies:
大多数浏览器支持最大为 4096 字节的 Cookie。
浏览器还限制站点可以在用户计算机上存储的 Cookie 的数量。大多数浏览器只允许每个站点存储 20 个 Cookie;注意这里的 20 个是指主键值,也就是 20 条 Cookies 记录,但是每个 Cookies 记录还可以包含若干子键,下面会详细解释。如果试图存储更多 Cookie,则最旧的 Cookie 便会被丢弃。有些浏览器还会对它们将接受的来自所有站点的 Cookie 总数作出绝对限制,通常为 300 个。
Cookies 可以包含一个主键, 主键再包含子键。比如 asp.net 中获取 Cookies 的格式是:
- Request.Cookies[key][subkey].ToString();
其中的 key 就是主键,subkey 就是主键关联的子键。
- My.Common.TestCookieInfo
- Pkid=999&TestValue=aaabbbcccdddeee
- localhost/
- 1536
- 3059603968
- 30021392
- 2348960464
- 30021191
- *
其中的 Pkid=999&TestValue=aaabbbcccdddeee 是 Cookies 的值,由于使用了 subkey=subvalue 的格式, 所以此 Cookies 是包含子键的。
在 Javascript 中给的 Cookie 是一个字符串,通过 document.cookies 获取。字符格式如下:
- My.Common.SubKey = Pkid = 999 & TestValue = aaabbbcccdddeee;
- SingleKey = SingleKeyValue
上面的字符串包含了两个 Cookies,一个是不包含子键的 SingleKey, 一个是包含 pkid 和 TextValue 两个子键的 My.Common.SubKey,两个 Cookie 通过 ";" 分割。
和所有的服务器端语言一样,Asp.Net 中使用集合类保存 Cookies 集合:
- public sealed class HttpCookieCollection : NameObjectCollectionBase
- {...}
通过 HttpResquest 和 HttpResponse 对象的 Cookies 属性,可以获取和写入当前页面的 Cookies。
Cookies 的值中可以保存除了 ";" 以外的标点符号。但是不能保存汉字。保存汉字会出现乱码。
所以对于 Cookies 中的内容要进行统一的编码和解码。为了在浏览器端和服务器端都能够进行解码和编码, 所以要统一使用 UTF 编码格式。
主要是因为 javascript 中只能使用 UTF 编码格式。
Cookies 的 Path 属性表示当前的 Cookies 可以作用在网站的那个路径下。
比如下面的两个同名的 Cookies:
允许存在两个同名但是 Path 不同的 Cookies。
无论是服务器端还是客户端,在获取时优先获取本页路径下面的 Cookies。
也就是说如果在、/chapter10 / 路径下面的页面, 获取 testKey 这个 Cookies 的值,则只能获取到 testValue222222 这个值。
如果保存 Cookies 时未设置过期时间, 则 Cookies 的过期时间为 "当前浏览器进程有效",即和 Session 一样关闭浏览器后则消失。在 asp.net 中还可以通过设置 HttpCookie 对象的过期时间为 DateTime.MinValue 来指定此 Cookies 为跟随浏览器生效。(这句话来之不易啊,在脑袋等人的帮助下才查到的。)
如果设置了过期时间并且大于当前时间,则会保存 Cookies 值。
如果设置了过期时间但是小于等于当前时间,则清除 Cookies 值。
有时我们会忽略 Cookies 与 Session 的关系。但是两者是密不可分的。
Session 的唯一标示:SessionID 是通常保存在 Cookies 中的(也可以保存在 URL 中)。对于 Asp.Net 而言,SessionID 保存在键值为 "ASP.NET_SessionId" 的 Cookies 中,如图:
因为 Cookies 的存储数量是有限制的,所以我们的系统在保存 Cookies 的时候一定要注意防止冲掉这一个关键的 Cookies。在下文介绍的最佳实践 - 以强对象方式保存 Cookies 的代码中特意对这个 Cookies 做了处理。
注意,在客户端使用 javascript 脚本无法获取 "ASP.NET_SessionId" 的 Cookies, 因为此 Cookies 在服务器端设置了 HttpOnly 属性为 true。
ASP.Net 中 HttpCookie 对象的 HttpOnly 属性 指定一个 Cookie 是否可通过客户端脚本访问。不能通过客户端脚本访问为 true;否则为 false。默认值为 false。此属性并不能完全阻止客户端在本地获取 cookies,但是可以增加通过脚本直接获取的难度。
Microsoft Internet Explorer 版本 6 Service Pack 1 和更高版本支持 Cookie 属性 HttpOnly。
在设置 Cookies 的属性时,有一个选项 Secure 用来控制 Cookie 的加密特性。
如果通过 SSL 连接 (HTTPS) 传输 Cookie,则为 true;否则为 false。默认为 false。
如果我们保存一个 Cookies 并设置加密,那么在非 HTTPS 的页面中,无论是使用 javascript 还是服务器端都无法获得此 Cookies。但是在本地依然可以看到此 Cookies 的存在。
如果 Ajax 请求访问一个服务器页面,此服务器页面是可以向用户浏览器写入 Cookies 和 Session 的。
在了解了 Cookies 的相关知识后,下面提出最佳的事件方法。其中包括客户端和服务器端两部分。
通常,我们使用 Request 和 Response 对象来直接操作 Cookies:
写入 Cookies:
- Response.Cookies["k1"].Value = "k1Value";
- Response.Cookies["k2"]["k2-1"] = "k2-1Value";
- Response.Cookies.Add(new HttpCookie("k3", "k3Value"));
读取 Cookies:
- Request["k1"] ;
- Request.Cookies["k1"].Value ;
- Request.Cookies["k2"]["k2-1"];
- Request.Cookies.Get(0).Value;
注意 Request["k1"] 这个大家熟悉的获取 get 和 post 参数的方法,同时还能够获取 Cookies 的值!
另外上面语句中的有些是必须通过 Value 属性访问的,有些则不需要。
下面提供一个可以以对象方式整体保存 Cookies 的工具类。并且只占用一条 Cookies,所有的属性都存在子键上。
- ///
- /// Cookies基类。将需要保存Cookies的数据类此类派生,可以将强类型对象在Cookies中的保存和读取。
- ///
- ///
- /// 2009.8.6 ziqiu.zhang created
- ///
- ///
- /// 假设MyCookiesInfo是从 从Cookies中获取对象:
- ///
- /// CookieInfo item = new CookieInfo(); //new以后已经从Cookies中构造了对象。
- ///
- /// 将对象保存在Cookies中:
- ///
- /// CookieInfo item = new CookieInfo();
- /// item.value = "test value";
- /// item.SetCookies("1"); //Cookies有效期为1天
- ///
- ///
- [System.Serializable]
- public class CookieInfo
- {
- #region ==================== Constructed Method ====================
- ///
- /// 构造函数
- ///
- public CookieInfo()
- {
- }
- #endregion
- #region ==================== Public Method ====================
- ///
- /// 得到当前Cookies的过期时间
- ///
- /// 过期时间
- public DateTime GetExpiresTime()
- {
- string cookieName = GetType().ToString();
- if (HttpContext.Current.Request.Cookies[cookieName] != null)
- {
- return HttpContext.Current.Request.Cookies[cookieName].Expires;
- }
- return DateTime.MinValue;
- }
- ///
- /// 保存Cookies,过期时间为浏览器关闭则失效。
- ///
- /// Cookies过期事件
- /// 是否保存成功
- public bool Save()
- {
- return this.Save(DateTime.MinValue);
- }
- ///
- /// 保存Cookies,需要指定过期时间。
- ///
- /// Cookies过期事件
- /// 是否保存成功
- public bool Save(DateTime expiresTime)
- {
- string CookieName = GetType().ToString();
- HttpCookie SessionCookie = null;
- //对 SessionId 进行备份.
- if (HttpContext.Current.Request.Cookies["ASP.NET_SessionId"] != null)
- {
- string SesssionId = HttpContext.Current.Request.Cookies["ASP.NET_SessionId"].Value.ToString();
- SessionCookie = new HttpCookie("ASP.NET_SessionId");
- SessionCookie.Value = SesssionId;
- }
- //设定cookie 过期时间.
- DateTime dtExpiry = expiresTime;
- HttpContext.Current.Response.Cookies[CookieName].Expires = dtExpiry;
- //设定cookie 域名.
- string domain = string.Empty;
- if (HttpContext.Current.Request.Params["HTTP_HOST"] != null)
- {
- //domain = "www.elong.com";
- domain = HttpContext.Current.Request.Params["HTTP_HOST"].ToString();
- }
- //如果是www.elong.com或多级域名,需要转化为elong.com
- if (domain.IndexOf(".") > -1)
- {
- string[] temp = domain.Split('.');
- if (temp.Length >= 3)
- {
- domain = temp[temp.Length - 2].Trim() + "." + temp[temp.Length - 1].Trim();
- }
- HttpContext.Current.Response.Cookies[CookieName].Domain = domain;
- }
- //把类的属性, 写入Cookie.
- PropertyInfo[] Propertys = GetType().GetProperties();
- foreach (PropertyInfo pi in Propertys)
- {
- object oj = pi.GetValue(this, null);
- Type type = pi.PropertyType;
- string valueStr = string.Empty;
- if (oj != null && oj.ToString() != string.Empty)
- {
- if (type == Type.GetType("System.DateTime"))
- {
- valueStr = ((DateTime)oj).ToString("yyyy/MM/dd HH:mm:ss", System.Globalization.DateTimeFormatInfo.InvariantInfo);
- }
- else
- {
- valueStr = oj.ToString();
- }
- HttpContext.Current.Response.Cookies[CookieName][pi.Name] = HttpUtility.UrlEncode(valueStr);
- }
- }
- //如果cookie总数超过20 个, 重写ASP.NET_SessionId, 以防Session 丢失.
- if (HttpContext.Current.Request.Cookies.Count > 20 && SessionCookie != null)
- {
- if (SessionCookie.Value != string.Empty)
- {
- HttpContext.Current.Response.Cookies.Remove("ASP.NET_SessionId");
- HttpContext.Current.Response.Cookies.Add(SessionCookie);
- }
- }
- return true;
- }
- ///
- /// 找回Cookie值
- ///
- public void Load()
- {
- string cookieValue = string.Empty;
- string CookieName = GetType().ToString();
- //通过遍历属性, 从cookie 中找回值, 回写到属性.
- PropertyInfo[] Propertys = GetType().GetProperties();
- foreach (PropertyInfo pi in Propertys)
- {
- try
- {
- cookieValue = HttpUtility.UrlDecode(HttpContext.Current.Request.Cookies[CookieName][pi.Name].ToString());
- }
- catch
- {
- cookieValue = string.Empty;
- }
- if (pi.CanWrite && cookieValue != null && cookieValue != string.Empty)
- {
- try
- {
- object obb = cookieValue;
- Type type = pi.PropertyType;
- obb = Convert.ChangeType(obb, type);
- pi.SetValue(this, obb, null);
- }
- catch { }
- }
- }
- }
- #endregion
- }
首先说明如何使用此类。
为想要保存在 Cookies 中的类建立模型,并且继承自 CookieInfo 即可。比如下面建立了 MyCookieInfo 类,其中包含属性 pkid,TestValue 和 TestDateTime:
- ///
- /// 保存Cookies的数据对象
- ///
- [System.Serializable]
- public class MyCookieInfo : CookieInfo
- {
- private int m_Pkid = 0;
- public int Pkid
- {
- get
- {
- return m_Pkid ;
- }
- set
- {
- m_Pkid = value ;
- }
- }
- private string m_TestValue = "";
- public string TestValue
- {
- get
- {
- return m_TestValue;
- }
- set
- {
- m_TestValue = value;
- }
- }
- private DateTime m_TestDateTime = DateTime.Now;
- public DateTime TestDateTime
- {
- get
- {
- return m_TestDateTime;
- }
- set
- {
- m_TestDateTime = value;
- }
- }
- }
接下来就可以使用对象的 Save 和 Load 方法保存和读取 Cookies:
- MyCookieInfo testCookies = new MyCookieInfo();
- testCookies.Pkid = 1;
- testCookies.TestValue = "中文测试";
- testCookies.Save();
- MyCookieInfo testCookies = new MyCookieInfo();
- testCookies.Load();
- this.lblMsg.Text = "Pkid:" + testCookies.Pkid.ToString();
- this.lblMsg.Text += ",TestValue:" + testCookies.TestValue.ToString();
- this.lblMsg.Text += ",TestDateTime:" +
- testCookies.TestDateTime.ToString("yyyy/MM/dd HH:mm:ss",
- System.Globalization.DateTimeFormatInfo.InvariantInfo);
现在我们已经可以将一个强类型的对象读取和保存 Cookies 了。
在客户端我们同样需要操作 Cookies。
下面是封装了的专门用于操作 Cookies 的 jQuery 工具函数。如果还有人不知道 jQuery 是什么,请参考我的 "从零开始学习 jQuery" 系列教程:
当然此工具函数稍加修改,就可以变成标准的 Javascript 函数。
下载地址:
工具函数说明:
方法签名: jQuery.cookie(name, subName, value, options)
方法说明:读取、写入、删除 Cookies
方法参数:
名称 | 说明 | 举例 |
name | cookies 的主键值 | 读取主键: 写入 cookies,值为字符串:
|
subName | 子键名称。在写入时请传递空或者 null | 读取子键:
写入 cookies,值为对象:
|
value | Cookies 值,可以是字符串或者对象。 如果是对象,则将对象的每个属性保存在 Cookies 子键。 |
参见上面实例。 |
options | 参数: expires:可以是数字或者 Data 类型的对象。 如果传入数字表示几天后过期。 path:路径,默认为域名根目录("/")。 secure:是否启用加密,默认为否。 |
指定过期时间:
1 天后过期:
|
很久没有发表文章了,作为博客园改版后我的第一篇文章, 希望对大家的工作能有所帮助。 欢迎拍砖!
来源: http://www.bubuko.com/infodetail-1953290.html