第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");


發佈了0 篇原創文章 · 獲贊 1 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章