第19章 客户端存储

 

随着 Web 应用程序的出现,也产生了对于能够直接在客户端上存储用户信息能力的要求。想法很合乎逻辑,属于某个特定用户的信息应该存在该用户的机器上。无论是登录信息、偏好设定或其他数据,web应用提供者发现他们在找各种方式将数据存在客户端上。这个问题的第一个方案是以 cookie 的形式出现的,cookie 是过去 Netscape Communications 公司创造的,并在一个标题为 "持久客户端状态 -- HTTP Cookies" 的标准中被阐述。今天,cookie 只是在客户端存储数据的其中一种选项。

19.1 cookie

HTTP Cookie ,通常直接叫做 cookie ,最初是在客户端用于存储会话信息的。该标准要求服务器对任意 HTTP 请求发送 Set-Cookie HTTP 头作为响应的一部分,其中包含会话信息。例如,这种服务器响应的头可能如下:

这个 HTTP 响应设置以 name 为名称、以 value 为值的一个 cookie ,名称和值在传送时都必须是 URL 编码的。浏览器会存储这样的会话信息,并在这之后,通过为每个请求添加 Cookie HTTP 头将信息发送回服务器,如下所示:

发送回服务器的额外信息可以用于唯一验证客户来自于发送的哪个请求。

19.1.1 限制

cookie 在性质上是绑定在特定的域名下的。当设定了一个 cookie 后,再给创建它的域名发送请求时,都会包含这个 cookie 。这个限制确保了存储在 cookie 中的信息只能让批准的接受者访问,而无法被其他域访问。

由于 cookie 是存在客户端计算机上的,还加入了一些限制确保 cookie 不会被恶意使用,同时不会占据太多磁盘空间。每个域的 cookie 总数是有限的,不过浏览器之间各有不同。如下所示。

  • IE6 以及更低版本限制每个域名最多 20 个 cookie。
  • IE7 和之后版本每个域名最多 50 个。IE7 最初是支持每个域名最大 20 个 cookie ,之后被微软的一个补丁所更新。
  • Firefox 限制每个域最多 50 个 cookie 。
  • Opera 限制每个域最多 30 个 cookie 。
  • Safari 和 Chrome 对于每个域的 cookie 数量限制没有硬性规定。
当超过单个域名限制之后还要再设置 cookie ,浏览器就会清除以前设置的 cookie 。IE 和 Opera 会删除最近最少 (LRU) 使用过的 cookie 以腾出空间给新设置的 cookie。Firefox 看上去好像是随机决定要清除哪个 cookie ,所以考虑 cookie 限制非常重要,以免出现不可预期的后果。
浏览器中对于 cookie 的尺寸也有限制。大多数浏览器都有大约 4096 字节 (加减1) 的长度限制。为了最佳的浏览器兼容性,最好将整个 cookie 长度限制在 4095 (含 4095) 字节以内。尺寸限制影响到一个域下所有的 cookie ,而并非每个 cookie 单独限制。
如果你尝试创建超过最大尺寸限制的 cookie ,那么该 cookie 会被悄无声息地丢掉。注意,虽然一个字符通常占用一个字节,但是多字节情况则有不同。

19.1.2 cookie 的成分

cookie 由浏览器保存的以下几块信息组成。
  • 名称 -- 一个唯一确定 cookie 的名称。cookie 名称是大小写不敏感的,所以 myCookie 和 MyCookie 被认为是同一个 cookie。然而,实践中最好将 cookie 名称看作是大小写敏感的,因为某些服务器会这样处理 cookie 。cookie 的名称必须是经过 URL 编码的。
  • 值 -- 存储在 cookie 中的字符串值。值必须被 URL 编码。
  • 域 -- cookie 对于哪个域是有效的。所有向该域发送的请求中都会包含这个 cookie 信息。这个值可以包含子域 (subdomain,如 www.wrox.com) ,也可以不包含它 (如 .wrox.com,则对于 wrox.com 的所有子域都有效)。如果没有明确设定,那么这个域会被认作来自设置 cookie 的那个域。
  • 路径 -- 对于指定域中的那个路径,应该向服务器发送 cookie 。例如,你可以指定 cookie 只有从 http://www.wrox.com/books/ 中才能访问,那么 http://www.wrox.com 的页面就不会发送 cookie 信息,即使请求都是来自同一个域的。
  • 失效时间 -- 表示 cookie 何时应该被删除的时间戳 (也就是,何时应该停止向服务器发送这个 cookie)。默认情况下,浏览器会话结束时即将所有 cookie 删除;不过也可以自己设置删除时间。这个值是个 GMT 格式的日期 (Wdy, DD-Mon-YYYY HH:MM:SS GMT), 用于指定应该删除 cookie 的准确时间。因此,cookie 可在浏览器关闭后依然保存在用户的机器上。如果你设置的失效日期是个以前的时间,则 cookie 被立刻删除。
  • 安全标志 -- 指定后,cookie 只有在使用 SSL 连接的时候才发送到服务器。例如,cookie 信息只能发送给 https://www.wrox.com,而 http://www.wrox.com 的请求则不能发送 cookie。
每一段信息都作为 Set-Cookie 头的一部分,使用 分号加空格分隔每一段,如下列所示:
该头信息指定了一个叫做 name 的 cookie ,它会在格林威治时间 2007年1月22日 7:10:24 失效,同时对于 www.wrox.com 和 wrox.com 的任何子域如 p2p.wrox.com 都有效。
secure 标志是 cookie 中唯一一个非名-值对的部分,直接包含一个 secure 单词。如下:
这里,创建了一个对于所有 wrox.com 的子域和域名下 (由 path 参数指定的) 所有页面都有效的 cookie 。因为设置了 secure 标志,这个 cookie 只能通过 SSL 连接才能传输。
尤其要注意,域、路径、失效时间和 secure 标志都是服务器给浏览器的指示,以指定何时应该发送 cookie 。这些参数并不会作为发送到服务器的 cookie 信息的一部分,只有名-值对才会被发送。

19.1.3 JavaScript 中的 cookie

在 JavaScript 中处理 cookie 有些复杂,因为其众所周知的蹩脚的接口, 即 BOM 的 document.cookie 属性。这个属性的独特之处在于它会因为使用它的方式不同而表现出不同的行为。当用来获取属性时,document.cookie 返回当前页面可用的 (根据 cookie 的域、路径、失效时间和安全设置) 所有 cookie 的字符串,一系列由分号隔开的名-值对,如下例所示:
name1=value1;name2=value2;name3=value3
所有名字和值都是经过 URL 编码的,所以必须使用 decodeURIComponenet() 来解码。
当用于设置值的时候,document.cookie 属性可以设置为一个新的 cookie 字符串。这个 cookie 字符串会被解释并添加到现有的 cookie 集合中。设置 document.cookie 并不会覆盖 cookie 除非设置的 cookie 的名称已经存在。设置 cookie 的格式如下,和 Set-Cookie 头中使用的一样格式:
name=value; expires=expiration_time; path=domain_path; domain=domain_name; secure
这些参数中,只有 cookie 的名字和值是必须的。下面是一个简单的例子:
document.cookie = "name=Nicholas";
这段代码创建了一个叫 name 的 cookie ,值为 Nicholas 。 当客户端每次向服务器端发送请求的时候,都会发送这个 cookie;当浏览器关闭的时候,它就会被删除。虽然这段代码没问题,但因为这里正好名称和值都无须进行编码,所以最好每次设置 cookie 时都像下面这个例子中一样使用 encodeURIComponent():
document.cookie = encodeURIComponent("name") + "=" + encodeURIComponent("Nicholas");
要给被创建的 cookie 指定额外的信息,只要将参数追加到该字符串,和 Set-Cookie 头中的格式一样,如下:
document.cookie = encodeURIComponent("name") + "=" + encodeURIComponent("Nicholas") + "; domain=.wrox.com; path=/";
由于 JavaScript 中读写 cookie 不是非常直观,我们常写一些函数来简化 cookie 的功能。基本的 cookie 操作有三种: 读取、写入和删除。它们在 CookieUtil 对象中如下表示:
var CookieUtil = {
	get: function(name){
		var cookieName = encodeURIComponent(name) + "=",
			cookieStart = document.cookie.indexOf(cookieName),
			cookieValue = null;
		if(cookieStart > -1){
			var cookieEnd = document.cookie.indexOf(";", cookieStart);
			if(cookieEnd == -1){
				cookieEnd = document.cookie.length;
			}
			cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
		}
		return cookieValue;
	},
	set: function(name, value, expires, path, domain, secure){
		var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);
		if(expires instanceof Date){
			cookieText += "; expires=" + expires.toGMTString();
		}
		if(path){
			cookieText += "; path=" + path;
		}
		if(domain){
			cookieText += "; domain=" + domain;
		}
		if(secure){
			cookieText += "; secure";
		}
		document.cookie = cookieText;
	},
	unset: function(name, path, domain, secure){
		this.set(name, "", new Date(0), path, domain, secure);
	}
};
 
 
CookieUtil.get() 方法根据 cookie 的名字获取相应的值。它是通过在 document.cookie 字符串中查找 cookie 名加上等于号的位置。如果找到了,那么使用 indexOf() 查找该位置之后的第一个分号 (表示了该 cookie 的结束位置)。如果没有找到分号,则表示该 cookie 是字符串中的最后一个,则余下的字符串都是 cookie 的值。该值使用 decodeURIComponent() 进行解码并最后返回。如果没有发现 cookie ,则返回 null 。
CookieUtil.set() 方法在页面上设置一个 cookie,接受几个参数:cookie 的名称,cookie 的值,可选的用于指定 cookie 何时应被删除的 Date 对象,cookie 的可选的 URL 路径,可选的域,以及可选的表示是否要添加 secure 的标志的 Boolean 值。参数是按照它们使用频率的多少来排列的,只有头两个是必需的。在这个方法中,名称和值都使用 encodeURIComponent() 进行了 URL 编码,并检查其他选项。如果 expires 参数是 Date 对象,那么会使用 Date 对象的 toGMTString() 方法正确格式化 Date 对象,并添加到 expires 选项上。方法的其他部分就是构造 cookie 字符串并将其设置到 document.cookie 中。
没有删除现存 cookie 的直接方法。所以,需要使用相同的路径、域和安全选项再次设置 cookie,并将失效时间设置为过去的时间。CookieUtil.unset() 方法可以处理这种事情。 它接受4个参数:要删除的 cookie 的名称,可选的路径参数,可选的域参数以及可选的安全参数。这些参数加上空字符串并设置失效时间为 1970年1月1日 (初始化为 0ms 的 Date 对象的值),传给 CookieUtil.set()。这样就能确保 cookie 被删除。
这些方法可以如下使用:
// 设置 cookie
CookieUtil.set("name", "Nicholas");
CookieUtil.set("book", "Professional JavaScript");
// 读值
alert(CookieUtil.get("name"));          // "Nicholas"
alert(CookieUtil.get("book"));        // "Professional JavaScript"
// 删除 cookie
CookieUtil.unset("name");
CookieUtil.unset("book");


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值