明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

ASP.NET中Cookie編程的基礎(chǔ)知識

[摘要]簡介  Cookie 為 Web 應(yīng)用程序保存用戶相關(guān)信息提供了一種有用的方法。例如,當(dāng)用戶訪問您的站點時,您可以利用 Cookie 保存用戶首選項或其他信息,這樣,當(dāng)用戶下次再訪問您的站點時,應(yīng)用程序就可以檢索以前保存的信息! ”疚母乓榻B Cookie 在 ASP.NET 應(yīng)用程序中的應(yīng)用,...

  簡介

  Cookie 為 Web 應(yīng)用程序保存用戶相關(guān)信息提供了一種有用的方法。例如,當(dāng)用戶訪問您的站點時,您可以利用 Cookie 保存用戶首選項或其他信息,這樣,當(dāng)用戶下次再訪問您的站點時,應(yīng)用程序就可以檢索以前保存的信息。

  本文概要介紹 Cookie 在 ASP.NET 應(yīng)用程序中的應(yīng)用,為您展示在 ASP.NET 中應(yīng)用 Cookie 的技術(shù)細(xì)節(jié),例如編寫 Cookie、然后再讀取它們。同時,還將為您介紹 Cookie 的各種特性和各種特殊情況,以及 ASP.NET 對 Cookie 的支持。

  什么是 Cookie?

  Cookie 是一小段文本信息,伴隨著用戶請求和頁面在 Web 服務(wù)器和瀏覽器之間傳遞。用戶每次訪問站點時,Web 應(yīng)用程序都可以讀取 Cookie 包含的信息。

  假設(shè)在用戶請求訪問您的網(wǎng)站 www.contoso.com 上的某個頁面時,您的應(yīng)用程序發(fā)送給該用戶的不僅僅是一個頁面,還有一個包含日期和時間的 Cookie。用戶的瀏覽器在獲得頁面的同時還得到了這個 Cookie,并且將它保存在用戶硬盤上的某個文件夾中。

  以后,如果該用戶再次訪問您站點上的頁面,當(dāng)該用戶輸入 URL www.contoso.com 時,瀏覽器就會在本地硬盤上查找與該 URL 相關(guān)聯(lián)的 Cookie。如果該 Cookie 存在,瀏覽器就將它與頁面請求一起發(fā)送到您的站點,您的應(yīng)用程序就能確定該用戶上一次訪問站點的日期和時間。您可以根據(jù)這些信息向用戶發(fā)送一條消息,也可以檢查過期時間或執(zhí)行其他有用的功能。

  Cookie 是與 Web 站點而不是與具體頁面關(guān)聯(lián)的,所以無論用戶請求瀏覽站點中的哪個頁面,瀏覽器和服務(wù)器都將交換 www.contoso.com 的 Cookie 信息。用戶訪問其他站點時,每個站點都可能會向用戶瀏覽器發(fā)送一個 Cookie,而瀏覽器會將所有這些 Cookie 分別保存。

  以上就是 Cookie 的基本工作原理。那么,Cookie 有哪些用途呢?最根本的用途是 Cookie 能夠幫助 Web 站點保存有關(guān)訪問者的信息。更概括地說,Cookie 是一種保持 Web 應(yīng)用程序連續(xù)性(即執(zhí)行“狀態(tài)管理”)的方法。瀏覽器和 Web 服務(wù)器除了在短暫的實際信息交換階段以外總是斷開的,而用戶向 Web 服務(wù)器發(fā)送的每個請求都是單獨處理的,與其他所有請求無關(guān)。然而在大多數(shù)情況下,都有必要讓 Web 服務(wù)器在您請求某個頁面時對您進(jìn)行識別。例如,購物站點上的 Web 服務(wù)器跟蹤每個購物者,以便站點能夠管理購物車和其他的用戶相關(guān)信息。因此 Cookie 的作用就類似于名片,它提供了相關(guān)的標(biāo)識信息,可以幫助應(yīng)用程序確定如何繼續(xù)執(zhí)行。

  使用 Cookie 能夠達(dá)到多種目的,所有這些目的都是為了使 Web 站點記住您。例如,一個實施民意測驗的站點可以簡單地利用 Cookie 作為布爾值,表示您的瀏覽器是否已經(jīng)參與了投票,從而避免您重復(fù)投票; 而那些要求用戶登錄的站點則可以通過 Cookie 來確定您是否已經(jīng)登錄過,這樣您就不必每次都輸入憑據(jù)。

  有關(guān) Cookie 的更多背景信息,建議您閱讀 Verizon Web 站點中的“How Internet Cookies Work”一文,地址為 http://www22.verizon.com/about/community/learningcenter/articles/displayarticle1/0,4065,1022z1,00.html(英文)。其作者詳細(xì)解釋了什么是 Cookie 以及 Cookie 是如何在瀏覽器和服務(wù)器之間交換信息的,他還全面總結(jié)了 Cookie 涉及的隱私問題。

  順便問一下,您是否想知道它們?yōu)槭裁幢环Q作“Cookie”?Jargon File(又稱為“The New Hacker's Dictionary”)版本 4.3.3 對這一術(shù)語的詞源給出了準(zhǔn)確的定義和合理的解釋。您可以在 http://www.catb.org/~esr/jargon/jargon.html#cookie(英文)找到相關(guān)的條目。

  在此后的內(nèi)容中,本文將假設(shè)您已經(jīng)知道什么是 Cookie,并且假設(shè)您已經(jīng)清楚為什么要在 ASP.NET 應(yīng)用程序中使用 Cookie。

  Cookie 的限制

  在開始討論 Cookie 的技術(shù)細(xì)節(jié)之前,我想先介紹一下 Cookie 應(yīng)用的幾條限制。大多數(shù)瀏覽器支持最多可達(dá) 4096 字節(jié)的 Cookie,如果要將為數(shù)不多的幾個值保存到用戶計算機上,這一空間已經(jīng)足夠大,但您不能用一個 Cookie 來保存數(shù)據(jù)集或其他大量數(shù)據(jù)。在實際應(yīng)用中,您可能并不希望在 Cookie 中保存大量的用戶信息,而只希望保存用戶編號或其他標(biāo)識符。之后,當(dāng)用戶再次訪問您的站點時,您就可以使用該用戶 ID 在數(shù)據(jù)庫中查找用戶的詳細(xì)信息。(有關(guān)保存用戶信息的說明,請參閱 Cookie 和安全性。)

  瀏覽器還限制了您的站點可以在用戶計算機上保存的 Cookie 數(shù)。大多數(shù)瀏覽器只允許每個站點保存 20 個 Cookie。如果試圖保存更多的 Cookie,則最先保存的 Cookie 就會被刪除。還有些瀏覽器會對來自所有站點的 Cookie 總數(shù)作出限制,這個限制通常為 300 個。

  您最可能遇到的 Cookie 限制是:用戶可以設(shè)置自己的瀏覽器,拒絕接受 Cookie。您很難解決這個問題,除非完全不使用 Cookie 而是通過其他機制來保存用戶相關(guān)信息。保存用戶信息的一種常用方法是會話狀態(tài),但會話狀態(tài)又依賴于 Cookie。這一點在后面的 Cookie 和會話狀態(tài)中闡述。

  注意:有關(guān)狀態(tài)管理和 Web 應(yīng)用程序中用于保存信息的選項的詳細(xì)信息,請參閱 Introduction to Web Forms State(英文)和 State Management Recommendations(英文)。
更一般的經(jīng)驗很可能是,盡管 Cookie 在應(yīng)用程序中非常有用,應(yīng)用程序也不應(yīng)該依賴于能夠保存 Cookie。利用 Cookie 可以做到錦上添花,但不要利用它們來支持關(guān)鍵功能。如果您的應(yīng)用程序必須使用 Cookie,則您可以通過測試來確定瀏覽器是否接受 Cookie。我在本文后面的檢查瀏覽器是否接受 Cookie 一節(jié)中簡單介紹了一種測試方法。

  編寫 Cookie

  您可以利用頁面的 Response(英文)屬性來編寫 Cookie,該屬性提供的對象使用戶可以將信息添加到由頁面向瀏覽器呈現(xiàn)的信息中。Response 對象支持一個名為 Cookies(英文)的集合,您可以向其中添加要寫入瀏覽器的 Cookie。

  注意:下面要討論的 Response 對象和 Request 對象分別是包含 HttpResponse(英文)和 HttpRequest(英文)類實例的頁面的屬性。要在文檔中查找 Response 和 Request 的信息,請參閱 HttpResponse 和 HttpRequest 下的內(nèi)容。

  在創(chuàng)建 Cookie 時,您需要指定幾個值。最初,您要指定 Cookie 的名稱和其中保存的值。您可以創(chuàng)建多個 Cookie,每個 Cookie 都必須具有唯一的名稱,以便日后讀取時識別。(Cookie 是按名稱保存的,所以如果您創(chuàng)建了兩個名稱相同的 Cookie,后保存的那一個將覆蓋前一個。)

  您可能還希望指定 Cookie 的過期日期和時間。Cookie 一般都寫入到用戶的磁盤,然后可能一直都留在磁盤上。因此,您可以指定 Cookie 過期的日期和時間。當(dāng)用戶再次訪問您的站點時,瀏覽器會先檢查您站點的 Cookie 集合,如果某個 Cookie 已經(jīng)過期,瀏覽器不會把這個 Cookie 隨頁面請求一起發(fā)送給服務(wù)器,而是刪除這個已經(jīng)過期的 Cookie。(您的站點可能已經(jīng)在用戶計算機上寫入了多個 Cookie,每個 Cookie 都有各自的過期日期和時間。) 請注意,由瀏覽器負(fù)責(zé)管理硬盤上的 Cookie,這將影響您在應(yīng)用程序中對 Cookie 的使用,我很快會介紹這方面的內(nèi)容。

  一個 Cookie 的有效期應(yīng)為多長?這取決于 Cookie 的用途,換句話說,取決于您的應(yīng)用程序需要 Cookie 值保持有效的時間有多長。如果利用 Cookie 統(tǒng)計網(wǎng)站的訪問者,您可以把有效期設(shè)置為 1 年,如果某個用戶已有一年時間未訪問您的站點,則可以把該用戶當(dāng)作新的訪問者; 如果利用 Cookie 來保存用戶的首選項,則可以把其設(shè)置為永遠(yuǎn)有效(例如 50 年后到期),因為定期重新設(shè)置首選項對用戶而言是比較麻煩的。有時,您可能需要編寫在數(shù)秒或數(shù)分鐘內(nèi)即過期的 Cookie。在本文后面的檢查瀏覽器是否接受 Cookie 一節(jié)中,我列舉了一個示例,該示例中創(chuàng)建的 Cookie 的實際有效期就只有幾秒。

  注意:不要忘記用戶隨時可以刪除自己計算機上的 Cookie,所以即使您保存了長期有效的 Cookie,用戶也可以自行決定將其全部刪除,同時清除保存在 Cookie 中的所有設(shè)置。

  如果沒有設(shè)置 Cookie 的有效期,還是可以創(chuàng)建 Cookie,但它不會保存到用戶的硬盤上,而是會成為用戶會話信息的一部分。如果用戶關(guān)閉瀏覽器或會話超時,該 Cookie 就會被刪除。這種非永久性的 Cookie 很適合用來保存只需短時間保存的信息,或者保存由于安全原因不應(yīng)該寫入客戶計算機磁盤的信息。例如,如果用戶使用的是一臺公用計算機,而您不希望把 Cookie 寫入這種計算機的磁盤上,這時就可以使用非永久性的 Cookie。

  您可以通過多種方法把 Cookie 添加到 Response.Cookies 集合中。以下示例介紹了兩種完成此任務(wù)的方法:

Response.Cookies("userName").Value = "mike"
Response.Cookies("userName").Expires = DateTime.Now.AddDays(1)

Dim aCookie As New HttpCookie("lastVisit")
aCookie.Value = DateTime.Now.ToString
aCookie.Expires = DateTime.Now.AddDays(1)
Response.Cookies.Add(aCookie) 

  該示例向 Cookies 集合中添加了兩個 Cookie,一個稱為“userName”,另一個稱為“l(fā)astVisit”。對于第一個 Cookie,我直接設(shè)置了 Response.Cookies 集合的值。您可以使用這種方法向集合中添加值,因為 Response.Cookies 是從 NameObjectCollectionBase(英文)類型的特殊集合派生得到的。

  對于第二個 Cookie,我創(chuàng)建了 Cookie 對象的一個實例(HttpCookie [英文] 類型),并設(shè)置了其屬性,然后通過 Add 方法把它添加到 Response.Cookies 集合。實例化 HttpCookie 對象時,您必須把 Cookie 名稱作為構(gòu)造函數(shù)的一部分進(jìn)行傳遞。

  這兩個示例完成了相同的任務(wù),即向瀏覽器寫入一個 Cookie。您要采用哪種方法主要取決于您的個人喜好。您可能會發(fā)現(xiàn)第二種方法在設(shè)置 Cookie 屬性方面要稍微容易一些,但同時您也會注意到兩者的差別并不是很大。

  在這兩種方法中,有效期值必須為 DateTime 類型。而“l(fā)astVisited”值也是日期/時間值。但在這種情況下,我必須把日期/時間值轉(zhuǎn)換為字符串,因為 Cookie 中的任何值最終都是以字符串的形式保存的。

  查看您的 Cookie

  您可能會發(fā)現(xiàn),了解創(chuàng)建 Cookie 的效果會對您很有幫助。而查看 Cookie 是比較容易的,因為它們都是文本文件,關(guān)鍵在于您能找到它們。不同的瀏覽器保存 Cookie 的方式也不同。我將介紹 Internet Explorer 是如何保存 Cookie 的。如果您使用的是其他瀏覽器,請查看該瀏覽器的幫助,以了解有關(guān) Cookie 處理方面的知識。

  查看 Cookie 的一個簡便方法是讓 Internet Explorer 為您查找。在 Internet Explorer 中,從“工具”菜單中選擇“Internet 選項”,在“常規(guī)”選項卡中單擊“設(shè)置”,然后單擊“查看文件”。Internet Explorer 將打開一個窗口,顯示所有的臨時文件,包括 Cookie。在窗口中查找以“Cookie:”開頭的文件 或查找文本文件。雙擊一個 Cookie,在默認(rèn)的文本文件中打開它。

  您也可以在硬盤上查找 Cookie 的文本文件,從而打開 Cookie。Internet Explorer 將站點的 Cookie 保存在文件名格式為 <user>@<domain>.txt 的文件中,其中 <user> 是您的帳戶名。例如,如果您的名稱為 mikepope,您訪問的站點為 www.contoso.com,那么該站點的 Cookie 將保存在名為 mikepope@www.contoso.txt 的文件中。(該文件名可能包含一個順序的編號,如 mikepope@www.contoso[1].txt。)

  這個 Cookie 文本文件是與用戶相關(guān)的,所以會按照帳戶分別保存。例如,在 Windows XP 中,您可以在如下所示的目錄中找到 Cookie 文件:

c:\Documents and Settings\<user>\Cookies

  要查找最新創(chuàng)建的 Cookie,可以按修改日期對目錄內(nèi)容進(jìn)行排序,并查找最近修改的文件。

  您可以使用文本編輯器打開 Cookie。如果該文件包含多個 Cookie,這些 Cookie 之間將用星號 (*) 分隔。每個 Cookie 的第一行是 Cookie 的名稱,第二行是值,其余各行則包含 Cookie 的日常處理信息,例如過期日期和時間。Cookie 中還有一個簡單的校驗和,如果更改 Cookie 名稱或值的長度,瀏覽器就會檢測到修改并刪除該 Cookie。

  多值 Cookie(子鍵)

  以上示例為每個要保存的值(用戶名、上次訪問時間)都使用了一個 Cookie 。您也可以在一個 Cookie 中保存多個名稱/值對。名稱/值對也稱作“鍵”或“子鍵”,具體取決于您讀取的內(nèi)容。(如果您熟悉 URL 的結(jié)構(gòu),就會發(fā)現(xiàn)子鍵與其中的查詢字符串非常相象。) 例如,如果不希望創(chuàng)建名為“userName”和“l(fā)astVisit”的兩個單獨的 Cookie,可以創(chuàng)建一個名為“userInfo”的 Cookie,并使其包含兩個子鍵:“userName”和“l(fā)astVisit”。

  有很多原因會讓我們用子鍵來代替單獨的 Cookie。最顯而易見的是,把相關(guān)或類似的信息放在一個 Cookie 中會比較有條理。另外,由于所有信息都在一個 Cookie 中,所以諸如有效期之類的 Cookie 屬性就適用于所有信息。(當(dāng)然,如果要為不同類型的信息指定不同的過期日期,就應(yīng)該把信息保存在單獨的 Cookie 中。)

  帶有子鍵的 Cookie 還可以幫助您減小 Cookie 的大小。如前面的 Cookie 的限制一節(jié)所述,Cookie 的總大小限制在 4096 字節(jié)以內(nèi),而且不能為一個網(wǎng)站保存超過 20 個 Cookie。利用帶子鍵的單個 Cookie,站點的 Cookie 數(shù)量就不會超過 20 個的限制。此外,一個 Cookie 會占用大約 50 個字符的基本空間開銷(用于保存有效期信息等),再加上其中保存的值的長度,其總和接近 4K 的限制。如果使用五個子鍵而不是五個單獨的 Cookie,您可以省去四個 Cookie 的基本空間開銷,總共能節(jié)省大約 200 個字節(jié)。

  要創(chuàng)建帶子鍵的 Cookie,您可以使用用于編寫單個 Cookie 的各種語法。以下示例顯示了編寫同一 Cookie 的兩種不同方法,其中的每個 Cookie 都帶有兩個子鍵:

Response.Cookies("userInfo")("userName") = "mike"
Response.Cookies("userInfo")("lastVisit") = DateTime.Now.ToString
Response.Cookies("userInfo").Expires = DateTime.Now.AddDays(1)

Dim aCookie As New HttpCookie("userInfo")
aCookie.Values("userName") = "mike"
aCookie.Values("lastVisit") = DateTime.Now.ToString
aCookie.Expires = DateTime.Now.AddDays(1)
Response.Cookies.Add(aCookie)

  控制 Cookie 有效范圍

  默認(rèn)情況下,一個站點的全部 Cookie 都一起保存在客戶機上,而且所有這些 Cookie 都會隨著對該站點發(fā)送的請求一起發(fā)送到服務(wù)器,也就是說,站點的每個頁面都能得到該站點的所有 Cookie。但有時候,您可能希望 Cookie 更具有針對性,這時,您可以通過兩種方法設(shè)置 Cookie 的有效范圍:

  把 Cookie 的有效范圍限制在服務(wù)器上的一個文件夾中,實際上這樣就將 Cookie 限制到站點上的某個應(yīng)用程序。

  把有效范圍設(shè)置為某個域,從而允許您指定域中的哪些子域可以訪問 Cookie。

  將 Cookie 限制到某個文件夾或應(yīng)用程序

  要將 Cookie 限制到服務(wù)器上的某個文件夾,請按如下方法設(shè)置 Cookie 的 Path 屬性:

Dim appCookie As New HttpCookie("AppCookie")
appCookie.Value = "written " & Now.ToString
appCookie.Expires = Now.AddDays(1)
appCookie.Path = "/Application1"
Response.Cookies.Add(appCookie)

  當(dāng)然,您也可以通過直接設(shè)置 Response.Cookies 來編寫 Cookie,如前文所述。

  路徑可以是站點根目錄下的物理路徑,也可以是虛擬根目錄。這樣一來,Cookie 就只能用于 Application1 文件夾或虛擬根目錄中的頁面。例如,如果您的站點名為 www.contoso.com,則前面示例中生成的 Cookie 就只能用于路徑為 http://www.contoso.com/Application1/ 的頁面以及該文件夾下的所有頁面,而不適用于其他應(yīng)用程序中的頁面,如 http://www.contoso.com/Application2/ 或 http://www.contoso.com/ 下的頁面。

  提示:通過對 Internet Explorer 和 Mozilla 瀏覽器進(jìn)行測試發(fā)現(xiàn),此處使用的路徑是區(qū)分大小寫的。一般而言,Windows 服務(wù)器上的 URL 不區(qū)分大小寫,但這種情況例外。您無法控制用戶如何在瀏覽器中輸入 URL,但是,如果您的應(yīng)用程序依賴于與特定路徑相關(guān)的 Cookie,則請確保您所創(chuàng)建的所有超鏈接中的 URL 與 Path 屬性值的大小寫相匹配。

  將 Cookie 的有效范圍限制到域

  默認(rèn)情況下,Cookie 與特定的域相關(guān)聯(lián)。例如,如果您的站點是 www.contoso.com,那么當(dāng)用戶向該站點請求頁面時,您編寫的 Cookie 就被發(fā)送到服務(wù)器。(有特定路徑值的 Cookie 除外,我在上一節(jié)剛剛解釋過。) 如果您的站點有子域(例如 contoso.com、sales.contoso.com 和 support.contoso.com),就可以把 Cookie 同特定的子域相關(guān)聯(lián)。為此,需要設(shè)置 Cookie 的 Domain 屬性,如下所示:

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

  如果按照這種方式設(shè)置域,則 Cookie 只能用于指定子域中的頁面。

  您也可以利用 Domain 屬性來創(chuàng)建可在多個子域中共享的 Cookie。例如,對域進(jìn)行如下設(shè)置:

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

  當(dāng)瀏覽器向服務(wù)器發(fā)送請求時,該服務(wù)器的 Cookie 會與請求一起發(fā)送。在 ASP.NET 應(yīng)用程序中,您可以使用 Request 對象來讀取 Cookie。Request 對象的結(jié)構(gòu)與 Response 對象的結(jié)構(gòu)基本相同,所以從 Request 對象中讀取 Cookie 的方法與向 Response 對象中寫入 Cookie 的方法非常類似。以下示例顯示了兩種方法,目的都是獲取名為“username”的 Cookie 的值并將值顯示在 Label 控件中:

If Not Request.Cookies("userName") Is Nothing Then
Label1.Text = Server.HtmlEncode(Request.Cookies("userName").Value)
End If

If Not Request.Cookies("userName") Is Nothing Then
Dim aCookie As HttpCookie = Request.Cookies("userName")
Label1.Text = Server.HtmlEncode(aCookie.Value)
End If 

  在獲取 Cookie 的值之前,應(yīng)該確保該 Cookie 確實存在。否則,您將得到一個 System.NullReferenceException(英文)異常。還需要注意的是,在頁面中顯示 Cookie 的內(nèi)容之前,我調(diào)用了 HttpServerUtility.HtmlEncode(英文)方法對 Cookie 的內(nèi)容進(jìn)行編碼。之所以這樣做,是因為我要顯示 Cookie 的內(nèi)容(一般您不會這樣做)而且要確保沒有任何惡意用戶在 Cookie 中添加了可執(zhí)行腳本。有關(guān) Cookie 安全性的詳細(xì)信息,請參閱 Cookie 和安全性。

  注意:由于不同的瀏覽器保存 Cookie 的方式也不同,所以同一臺計算機上的不同瀏覽器不一定能夠相互讀取各自的 Cookie。例如,如果使用 Internet Explorer 測試一個頁面,然后再使用其他瀏覽器進(jìn)行測試,那么后者就不會找到 Internet Explorer 保存的 Cookie。當(dāng)然,大多數(shù)人一般都是使用同一種瀏覽器進(jìn)行 Web 交互的,因此在大多數(shù)情況下不會出現(xiàn)問題。但有時還是會遇到問題,比如您要測試應(yīng)用程序?qū)g覽器的兼容性。

  讀取 Cookie 中子鍵值的方法與設(shè)置該值的方法類似。以下是獲取子鍵值的一種方法:

If Not Request.Cookies("userInfo") Is Nothing Then
Label1.Text = _
Server.HtmlEncode(Request.Cookies("userInfo")("userName"))
Label2.text = _
Server.HtmlEncode(Request.Cookies("userInfo")("lastVisit"))
End If

  在上面的示例中,我獲取的是子鍵“l(fā)astVist”的值,在此前的討論中我把該值設(shè)置為 DateTime 值的字符串表示形式。請記住,Cookie 是用字符串的形式保存值的,所以要將 lastVisit 值用作日期,就必須對其進(jìn)行轉(zhuǎn)換:

Dim dt As DateTime
dt = CDate(Request.Cookies("userInfo")("lastVisit"))

  Cookie 中子鍵的類型是 NameValueCollection(英文)類型的集合。因此,另一種獲取單個子鍵的方法是先獲取子鍵集合,然后按名稱提取子鍵的值,如下所示:

If Not Request.Cookies("userInfo") Is Nothing Then
Dim UserInfoCookieCollection As _
System.Collections.Specialized.NameValueCollection
UserInfoCookieCollection = Request.Cookies("userInfo").Values
Label1.Text = Server.HtmlEncode(UserInfoCookieCollection("userName"))
Label2.Text = Server.HtmlEncode(UserInfoCookieCollection("lastVisit"))
End If

  就像設(shè)置 Cookie 一樣,使用哪種方法讀取 Cookie 也由您自己決定。

  什么是有效期?

  您可以讀取 Cookie 的名稱和值,除此以外,需要了解的有關(guān) Cookie 的信息并不是很多。雖然您可以獲取 Domain 和 Path 屬性,但是這些屬性的用途很有限。例如,您可以讀取 Domain 屬性,但如果您的頁面與 Cookie 不在相同的域,您根本就不會在頁面的位置接收到該 Cookie。

  您無法讀取的是 Cookie 的過期日期和時間。事實上,當(dāng)瀏覽器向服務(wù)器發(fā)送 Cookie 信息時,瀏覽器并未將過期信息包括在內(nèi)。您可以讀取 Expires 屬性,但總是返回為零的日期/時間值。

  在前面的編寫 Cookie 一節(jié)中,我已經(jīng)講過,是瀏覽器負(fù)責(zé)管理 Cookie 的,Expires 屬性就很好地印證了這一點。Expires 屬性的主要作用是幫助瀏覽器執(zhí)行有關(guān) Cookie 保存的日常管理。從服務(wù)器的角度來看,Cookie 要么存在要么不存在,所以對服務(wù)器而言,有效期并不是有用的信息。所以,瀏覽器在發(fā)送 Cookie 時并不提供此信息。如果您需要 Cookie 的過期日期,就必須重新設(shè)置,關(guān)于這一點我將在修改和刪除 Cookie 中介紹。

  更確切地說,您可以在向瀏覽器發(fā)送 Cookie 之前讀取已在 Response 對象中設(shè)置的 Expires 屬性,但您無法從返回的 Request 對象中獲取有效期信息。

  讀取 Cookie 集合

  前面的示例假設(shè)您要讀取名稱已知的 Cookie。有時,您可能需要讀取可供頁面使用的所有 Cookie。要讀取可供頁面使用的所有 Cookie 的名稱和值,您可以利用如下代碼遍歷 Request.Cookies 集合:

Dim i As Integer
Dim output As String = ""
Dim aCookie As HttpCookie
For i = 0 to Request.Cookies.Count - 1
aCookie = Request.Cookies(i)
output &= "Cookie 名稱 = " & Server.HtmlEncode(aCookie.Name) & "<br>"
output &= "Cookie 值 = " & Server.HtmlEncode(aCookie.Value) & _
& "<br><br>"
Next
Label1.Text = output

  注意:運行此代碼時,您很可能會看到一個名為“ASP.NET_SessionId”的 Cookie,ASP.NET 用這個 Cookie 來保存您的會話的唯一標(biāo)識符。這個會話 Cookie 不會永久保存到您的硬盤上。有關(guān)會話 Cookie 的詳細(xì)信息,請參閱本文后面的 Cookie 和會話狀態(tài)。
前面的示例有一個限制:如果 Cookie 有子鍵,就會以一個單獨的名稱/值字符串來顯示子鍵。Cookie 的 HasKeys(英文)屬性可以告訴您該 Cookie 是否有子鍵。如果有子鍵,您可以在子鍵集合中向下鉆取,獲取各個子鍵的名稱和值。

  如前文所述,您可以從 Cookie 屬性 Values(英文)中獲取有關(guān)子鍵的信息,該屬性是類型 NameValueCollection 的集合。您可以根據(jù)索引值從 Values 集合中直接讀取子鍵值。相應(yīng)的子鍵值可以從 Values 集合的成員 AllKeys(英文)中得到,該成員將返回一個字符串集合。

  以下示例是對前一示例的修改。示例中使用 HasKeys 屬性來測試子鍵,如果檢測到子鍵,就從 Values 集合中獲取子鍵:

Dim i As Integer
Dim j As Integer
Dim output As String = ""
Dim aCookie As HttpCookie
Dim subkeyName As String
Dim subkeyValue As String
For i = 0 To Request.Cookies.Count - 1
aCookie = Request.Cookies(i)
output &= "名稱 = " & aCookie.Name & "<br>"
If aCookie.HasKeys Then
For j = 0 To aCookie.Values.Count - 1
subkeyName = Server.HtmlEncode(aCookie.Values.AllKeys(j))
subkeyValue = Server.HtmlEncode(aCookie.Values(j))
output &= "子鍵名稱 = " & subkeyName & "<br>"
output &= "子鍵值 = " & subkeyValue & "<br><br>"
Next
Else
output &= "值 = " & Server.HtmlEncode(aCookie.Value) & "<br><br>"
End If
Next
Label1.Text = output

  您也可以把子鍵作為 NameValueCollection 對象進(jìn)行提取,如下所示:

If aCookie.HasKeys Then
Dim CookieValues As _
System.Collections.Specialized.NameValueCollection = aCookie.Values
Dim CookieValueNames() As String = CookieValues.AllKeys
For j = 0 To CookieValues.Count – 1
subkeyName = Server.HtmlEncode(CookieValueNames(j))
subkeyValue = Server.HtmlEncode(CookieValues(j))
output &= "子鍵名稱 = " & subkeyName & "<br>"
output &= "子鍵值 = " & subkeyValue & "<br><br>"
Next
Else
output &= "值 = " & aCookie.Value & "<br><br>"
End If

  注意:請記住,我之所以調(diào)用 Server.HtmlEncode 方法,只是因為我要在頁面上顯示 Cookie 的值。如果您只是測試 Cookie 的值,就不必在使用前對其進(jìn)行編碼。

  修改和刪除 Cookie

  有時,您可能需要修改某個 Cookie,更改其值或延長其有效期。(請記住,由于瀏覽器不會把有效期信息傳遞到服務(wù)器,所以您無法讀取 Cookie 的過期日期。)

  當(dāng)然,實際上您并不是直接更改 Cookie。盡管您可以從 Request.Cookies 集合中獲取 Cookie 并對其進(jìn)行操作,但 Cookie 本身仍然存在于用戶硬盤上的某個地方。因此,修改某個 Cookie 實際上是指用新的值創(chuàng)建新的 Cookie,并把該 Cookie 發(fā)送到瀏覽器,覆蓋客戶機上舊的 Cookie。

  以下示例說明了如何更改用于儲存站點訪問次數(shù)的 Cookie 的值:

Dim counter As Integer
If Request.Cookies("counter") Is Nothing Then
counter = 0
Else
counter = CInt(Request.Cookies("counter").Value)
End If
counter += 1
Response.Cookies("counter").Value = counter.ToString
Response.Cookies("counter").Expires = DateTime.Now.AddDays(1) 

  或者:

Dim ctrCookie As HttpCookie
Dim counter As Integer
If Request.Cookies("counter") Is Nothing Then
ctrCookie = New HttpCookie("counter")
Else
ctrCookie = Request.Cookies("counter")
End If
counter = CInt(ctrCookie.Value) + 1
ctrCookie.Value = counter.ToString
ctrCookie.Expires = DateTime.Now.AddDays(1)
Response.Cookies.Add(ctrCookie)

  刪除 Cookie

  刪除 Cookie(即把該 Cookie 從用戶的硬盤上物理刪除)是修改 Cookie 的一種形式。由于 Cookie 位于用戶的計算機中,所以您無法直接將其刪除。但是,您可以讓瀏覽器為您刪除 Cookie。修改 Cookie 的方法前面已經(jīng)介紹過(即用相同的名稱創(chuàng)建一個新的 Cookie),不同的是將其有效期設(shè)置為過去的某個日期。當(dāng)瀏覽器檢查 Cookie 的有效期時,就會刪除這個已過期的 Cookie。

  所以,刪除 Cookie 的方法與創(chuàng)建該 Cookie 的方法是相同的,只不過要把其有效期設(shè)置為過去的某個日期。以下示例比刪除單個 Cookie 要稍微有趣一些,它使用的方法可以刪除當(dāng)前域的所有 Cookie:

Dim i As Integer
Dim cookieName As String
Dim limit As Integer = Request.Cookies.Count - 1
For i = 0 To limit
aCookie = Request.Cookies(i)
aCookie.Expires = DateTime.Now.AddDays(-1)
Response.Cookies.Add(aCookie)
Next 

  修改或刪除子鍵

  修改單個子鍵的方法與最初創(chuàng)建它的方法相同:

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

  比較復(fù)雜的問題是如何刪除單個子鍵。您不能只是簡單地重新設(shè)置 Cookie 的過期日期,因為這樣只能刪除整個 Cookie 而不能刪除單個子鍵。實際的解決方案是對包含子鍵的 Cookie 的 Values 集合進(jìn)行操作。首先,通過從 Request.Cookies 對象中獲取 Cookie 來重新創(chuàng)建 Cookie。然后,您就可以調(diào)用 Values 集合的 Remove 方法,將要刪除的子鍵名稱傳遞到 Remove 方法。接下來,您通?梢詫⑿薷暮蟮 Cookie 添加到 Response.Cookies 集合,以便將修改后的 Cookie 發(fā)送回瀏覽器。

  以下代碼顯示了如何刪除子鍵。在示例中,要刪除的子鍵的名稱在變量中指定。

Dim subkeyName As String
subkeyName = "userName"
Dim aCookie As HttpCookie = Request.Cookies("userInfo")
aCookie.Values.Remove(subkeyName)
aCookie.Expires = DateTime.Now.AddDays(1)
Response.Cookies.Add(aCookie) 

  Cookie 與安全性

  在使用 Cookie 時,您必須意識到其固有的安全弱點。我所指的安全性并不是隱私問題,正如我在前面的什么是 Cookie?中所述,隱私在更大程度上是某些用戶面對的問題:這些用戶很關(guān)心 Cookie 中的信息是如何被使用的。而 Cookie 的安全性問題與從客戶機獲取數(shù)據(jù)的安全性問題類似。對于初學(xué)者,就應(yīng)用程序而言,Cookie 是用戶輸入的另一種形式,因而很容易被他人非法獲取和利用。由于 Cookie 保存在用戶自己的計算機上,所以用戶至少可以看到您保存在 Cookie 中的信息。如果用戶愿意,還能在瀏覽器向您發(fā)送 Cookie 之前修改該 Cookie。

  所以,您千萬不要在 Cookie 中保存保密信息 - 用戶名、密碼、信用卡號等等。在 Cookie 中不要保存不應(yīng)該由用戶掌握的內(nèi)容,也不要保存可能被其他竊取 Cookie 的人控制的內(nèi)容。

  同樣,要對從 Cookie 中得到的任何信息都持懷疑態(tài)度。不要認(rèn)為得到的數(shù)據(jù)就是您當(dāng)初設(shè)想的信息。處理 Cookie 值時采用的安全措施應(yīng)該與處理 Web 頁面中用戶鍵入的數(shù)據(jù)時采用的安全措施相同。例如,在頁面中顯示值之前,我會對 Cookie 中的內(nèi)容進(jìn)行 HTML 編碼。這是一種標(biāo)準(zhǔn)的方法,可以在顯示之前凈化從用戶處得到的信息,對 Cookie 的處理與此相同。

  另一個需要關(guān)心的問題是,Cookie 是以純文本的形式在瀏覽器和服務(wù)器之間傳送的,任何可以截取 Web 通信的人都可以讀取 Cookie。您可以對 Cookie 的屬性進(jìn)行設(shè)置,使其只能在使用安全套接字層(SSL,又稱 https://)的連接上傳輸。SSL 并不能防止保存在用戶計算機上的 Cookie 被他人讀取或操作,但它能防止 Cookie 在傳輸途中被他人截取。本文不討論 SSL,但您必須清楚,您可以對 Cookie 進(jìn)行傳輸保護(hù)。有關(guān) SSL 的詳細(xì)信息,請參閱 Secure Sockets Layer: Protect Your E-Commerce Web Site with SSL and Digital Certificates(英文)。

  面對這些安全問題,如何才能安全地使用 Cookie?您可以在 Cookie 中保存一些不重要的數(shù)據(jù),如用戶首選項或其他對應(yīng)用程序沒有重大影響的信息。如果確實需要把某些敏感信息(如用戶 ID)保存在 Cookie 中,就對這些信息進(jìn)行加密。一種可行的方法是利用 ASP.NET Forms Authentication 實用程序創(chuàng)建一個身份驗證票據(jù),作為 Cookie 保存。本文不討論有關(guān)加密的問題,但是,如果您需要在 Cookie 中保存敏感信息,就應(yīng)該試著采取措施來隱藏信息,防止被他人盜用。

  在 Mitigating Cross-site Scripting With HTTP-only Cookies(英文)一文中,您可以了解到更多有關(guān) Cookie 及其安全弱點的信息。

  檢查瀏覽器是否接受 Cookie

  我在前面的 Cookie 的限制一節(jié)中曾經(jīng)提到一個潛在問題,即用戶可以設(shè)置自己的瀏覽器拒絕接受 Cookie。如何才能知道您是否可以讀寫 Cookie?在不能寫入 Cookie 時不會出現(xiàn)任何錯誤(例如 Response.Cookies 不會拋出異常),因為服務(wù)器并不跟蹤呈現(xiàn)頁面后出現(xiàn)的情況。瀏覽器同樣不會向服務(wù)器發(fā)送任何有關(guān)其當(dāng)前的 Cookie 設(shè)置的信息。(也許您需要了解,但 HttpBrowserCapabilities.Cookies Property [英文] 屬性并不會告訴您 Cookie 是否被啟用,而只能告訴您當(dāng)前的瀏覽器是否支持 Cookie。)

  一種確定瀏覽器是否接受 Cookie 的方法是先編寫一個 Cookie,然后再嘗試讀取這個 Cookie。如果不能讀取這個 Cookie,則可以認(rèn)為該瀏覽器不接受 Cookie。

  我編寫了一個簡單的示例來說明如何測試 Cookie 是否被接受。該示例包含兩個頁面。在第一個頁面中,我編寫了一個 Cookie,然后把瀏覽器重新定向到第二個頁面。第二個頁面嘗試讀取這個 Cookie,轉(zhuǎn)而將瀏覽器重新定向到第一個頁面,并向 URL 添加一個帶有測試結(jié)果的查詢字符串變量。

  第一個頁面的代碼如下:
Sub Page_Load()
If Not Page.IsPostBack Then
If Request.QueryString("AcceptsCookies") Is Nothing Then
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 = "接受 Cookie = " & _
Request.QueryString("AcceptsCookies")
End If
End If
End Sub

  第一個頁面測試是否有回信,如果沒有,就搜索包含測試結(jié)果的查詢字符串變量 (AcceptsCookies)。如果沒有找到查詢字符串變量,則表示測試還沒有完成,代碼就寫出一個名為“TestCookie”的 Cookie。寫出 Cookie 之后,示例調(diào)用 Response.Redirect 來切換到測試頁面 (TestForCookies.aspx)。附加到測試頁面的 URL 的是名為 redirect 的查詢字符串變量,該變量中包含了當(dāng)前頁面的 URL,這樣就能在執(zhí)行測試后把重定向到該頁面。

  測試頁面可以完全由代碼組成,不需要包含控件。以下就是我使用的代碼:

Sub Page_Load()
Dim redirect As String = Request.QueryString("redirect")
Dim acceptsCookies As String
' 是否接受 Cookie?
If Request.Cookies("TestCookie") Is Nothing Then
' 沒有 Cookie,因此不需要接受
acceptsCookies = 0
Else
acceptsCookies = 1
' 刪除測試 Cookie
Response.Cookies("TestCookie").Expires = _
DateTime.Now.AddDays(-1)
End If
Response.Redirect(redirect & "?AcceptsCookies=" & acceptsCookies, _
True)
End Sub
 

  讀取 redirect 查詢字符串變量后,代碼就嘗試讀取 Cookie。為了實現(xiàn)日常管理,如果該 Cookie 確實存在,就會被立即刪除。測試完成后,代碼從 redirect 查詢字符串變量傳遞的 URL 構(gòu)造一個新的 URL。新的 URL 也包括一個包含測試結(jié)果的查詢字符串變量。最后一步是使用新的 URL 將瀏覽器重定向到原來的頁面。

  這個示例十分簡單,但說明了通過運行程序并查看結(jié)果來進(jìn)行測試的基本原則。其中最需要改進(jìn)的地方是要永久保存 Cookie 測試結(jié)果,這樣用戶就不必在每次瀏覽原始頁面時都重復(fù)進(jìn)行測試。但是,實際上并不能做到這一點。Cookie 不會起作用,原因是顯而易見的。另一種可能是把測試結(jié)果保存在會話狀態(tài)中,但在默認(rèn)情況下,會話狀態(tài)也依賴于 Cookie,而如果瀏覽器不接受 Cookie,會話狀態(tài)也不會起作用。解決后一個問題的辦法是采用無 Cookie 的會話狀態(tài)。下一節(jié)我將簡要介紹會話狀態(tài)如何與 Cookie 協(xié)作。

  Cookie 和會話狀態(tài)

  當(dāng)用戶訪問您的站點時,服務(wù)器會為該用戶創(chuàng)建唯一的會話,會話將一直延續(xù)到用戶訪問結(jié)束。對于每個會話,ASP.NET 都維護(hù)一種基于服務(wù)器的結(jié)構(gòu)(會話狀態(tài)),在該結(jié)構(gòu)中應(yīng)用程序可以保存用戶的相關(guān)信息。有關(guān)詳細(xì)信息,請參閱 Session State(英文)。

  ASP.NET 需要能跟蹤每個用戶的會話 ID,這樣才能把用戶映射到服務(wù)器上的會話狀態(tài)信息。默認(rèn)情況下,ASP.NET 使用一個非永久性的 Cookie 來保存會話狀態(tài)。如果您使用讀取 Cookie 一節(jié)的“讀取 Cookie 集合”中的示例,您可能就會在 Cookie 中發(fā)現(xiàn)一個會話狀態(tài) Cookie。

  但是如果用戶禁用了瀏覽器的 Cookie,會話狀態(tài)就不能使用 Cookie 來保存會話 ID,會話狀態(tài)也不會起作用。這就是為什么我在前面的檢查瀏覽器是否接受 Cookie 中說,無法在 Cookie 測試完畢后把測試結(jié)果實際保存在會話狀態(tài)中,因為沒有 Cookie 就沒有會話狀態(tài)。

  ASP.NET 提供了一種解決方案,即利用無 Cookie 的會話。您可以配置自己的應(yīng)用程序,不在 Cookie 中保存會話 ID,而是在站點頁面的 URL 中保存。會話 ID 保存在 URL 中,也就是 ASP.NET 將 ID 保存在瀏覽器中,從而能夠在用戶請求其他頁面時取回 ID。

  無 Cookie 會話可以避免瀏覽器拒絕 Cookie 的問題,使您能夠使用會話狀態(tài)。如果您的應(yīng)用程序依賴于會話狀態(tài),您可能就需要對其進(jìn)行配置,使它能使用無 Cookie 會話。但是,在某些情況下,如果用戶與其他人共享 URL - 可能是用戶通過電子郵件將 URL 發(fā)送給同事,而該用戶的會話仍然處于激活狀態(tài) - 那么最終這兩個用戶可能共享同一個會話,結(jié)果將難以預(yù)料。