細說Cookie

汪磊發表於2013-08-31

Cookie雖然是個很簡單的東西,但它又是WEB開發中一個很重要的客戶端資料來源,而且它可以實現擴充套件性很好的會話狀態,所以我認為每個WEB開發人員都有必要對它有個清晰的認識。本文將對Cookie這個話題做一個全面的描述,也算是我對Cookie的認識總結。

Cookie 概述

Cookie是什麼? Cookie 是一小段文字資訊,伴隨著使用者請求和頁面在 Web 伺服器和瀏覽器之間傳遞。Cookie 包含每次使用者訪問站點時 Web 應用程式都可以讀取的資訊。

為什麼需要Cookie? 因為HTTP協議是無狀態的,對於一個瀏覽器發出的多次請求,WEB伺服器無法區分是不是來源於同一個瀏覽器。所以,需要額外的資料用於維護會話。 Cookie 正是這樣的一段隨HTTP請求一起被傳遞的額外資料。

Cookie能做什麼? Cookie只是一段文字,所以它只能儲存字串。而且瀏覽器對它有大小限制以及它會隨著每次請求被髮送到伺服器,所以應該保證它不要太大。 Cookie的內容也是明文儲存的,有些瀏覽器提供介面修改,所以,不適合儲存重要的或者涉及隱私的內容。

Cookie 的限制。 大多數瀏覽器支援最大為 4096 位元組的 Cookie。由於這限制了 Cookie 的大小,最好用 Cookie 來儲存少量資料,或者儲存使用者 ID 之類的識別符號。使用者 ID 隨後便可用於標識使用者,以及從資料庫或其他資料來源中讀取使用者資訊。瀏覽器還限制站點可以在使用者計算機上儲存的 Cookie 的數量。大多數瀏覽器只允許每個站點儲存 20 個 Cookie;如果試圖儲存更多 Cookie,則最舊的 Cookie 便會被丟棄。有些瀏覽器還會對它們將接受的來自所有站點的 Cookie 總數作出絕對限制,通常為 300 個。

通過前面的內容,我們瞭解到Cookie是用於維持服務端會話狀態的,通常由服務端寫入,在後續請求中,供服務端讀取。下面本文將按這個過程看看Cookie是如何從服務端寫入,最後如何傳到服務端以及如何讀取的。

Cookie的寫、讀過程

在Asp.net中,讀寫Cookie是通過使用HttpCookie類來完成的,它的定義如下:

public sealed class HttpCookie
{
    // 獲取或設定將此 Cookie 與其關聯的域。預設值為當前域。
    public string Domain { get; set; }
    // 獲取或設定此 Cookie 的過期日期和時間(在客戶端)。
    public DateTime Expires { get; set; }
    // 獲取一個值,通過該值指示 Cookie 是否具有子鍵。
    public bool HasKeys { get; }
    // 獲取或設定一個值,該值指定 Cookie 是否可通過客戶端指令碼訪問。
    // 如果 Cookie 具有 HttpOnly 屬性且不能通過客戶端指令碼訪問,則為 true;否則為 false。預設為 false。
    public bool HttpOnly { get; set; }
    // 獲取或設定 Cookie 的名稱。
    public string Name { get; set; }
    // 獲取或設定要與當前 Cookie 一起傳輸的虛擬路徑。預設值為當前請求的路徑。
    public string Path { get; set; }
    // 獲取或設定一個值,該值指示是否使用安全套接字層 (SSL)(即僅通過 HTTPS)傳輸 Cookie。
    public bool Secure { get; set; }
    // 獲取或設定單個 Cookie 值。預設值為空引用。
    public string Value { get; set; }
    // 獲取單個 Cookie 物件所包含的鍵值對的集合。
    public NameValueCollection Values { get; }
    // 獲取 System.Web.HttpCookie.Values 屬性的快捷方式。
    public string this[string key] { get; set; }
}

Cookie寫入瀏覽器的過程:我們可以使用如下程式碼在Asp.net專案中寫一個Cookie 併傳送到客戶端的瀏覽器(為了簡單我沒有設定其它屬性)。

HttpCookie cookie = new HttpCookie("MyCookieName", "string value");
Response.Cookies.Add(cookie);

我想很多人都寫過類似的程式碼,但是,大家有沒有想過:Cookie最後是如何傳送到客戶端的呢?我們開啟Fiddler來看一下吧。

細說Cookie

從上圖,您應該能發現,我們在服務端寫的Cookie,最後其實是通過HTTP的響應頭這種途徑傳送到客戶端的。每一個寫入動作,都會產生一個【Set-Cookie】的響應頭。
瀏覽器正是在每次獲取請求的響應後,檢查這些頭來接收Cookie的。

Asp.net獲取Cookie的過程:我們可以使用如下程式碼在Asp.net專案中讀取一個Cookie

HttpCookie cookie = Request.Cookies["MyCookieName"];
if( cookie != null )
    labCookie1.Text = cookie.Value;
else
    labCookie1.Text = "未定義";

程式碼同樣也很簡單,還是類似的問題:大家有沒有想過,Cookie是如何傳到服務端的呢?我們還是繼續使用Fiddler來尋找答案吧。

細說Cookie

從圖片中,我們可以發現,Cookie是放在請求頭中,傳送到服務端的。如果你一直重新整理頁面,就能發現, 每次HTTP請求,Cookie都會被髮送。當然了,瀏覽器也不是傳送它所接收到的所有Cookie,它會檢查當前要請求的域名以及目錄,只要這二專案與Cookie對應的Domain和Path匹配,才會傳送。對於Domain則是按照尾部匹配的原則進行的。
所以,我在訪問 www.cnblogs.com 時,瀏覽器並不會將我在瀏覽 www.163.com 所接收到的 Cookie 發出去。

刪除Cookie:其實就是在寫Cookie時,設定Expires為一個【早於現在時間的時間】。也就是:設定此Cookie已經過期,瀏覽器接收到這個Cookie時,便會刪除它們。

HttpCookie cookie = new HttpCookie("MyCookieName", null);
cookie.Expires = new DateTime(1900, 1, 1);
Response.Cookies.Add(cookie);

使用Cookie儲存複雜物件

前面的示例程式碼大致演示了Cookie的讀寫操作。不過,我們平時可能希望將更復雜的【自定義型別】通過Cookie來儲存,那麼又該如何操作呢?對於這個問題,我們定義一個型別來看看如何處理。

public class DisplaySettings 
{
    public int Style;

    public int Size;
    
    public override string ToString()
    {
        return string.Format("Style = {0}, Size = {1}", this.Style, this.Size);
    }    
}

上面的程式碼,我定義一個型別,用於儲存使用者在瀏覽頁面時的顯示設定。接下來,我將介紹二種方法在Cookie中儲存並讀取它們。

方法-1,經典做法。(注意前面給出的HttpCookie定義程式碼中的最後二個成員)

private void WriteCookie_2a()
{
    DisplaySettings setting = new DisplaySettings { Style = 1, Size = 24 };

    HttpCookie cookie = new HttpCookie("DisplaySettings1");
    cookie["Style"] = setting.Style.ToString();
    cookie["Size"] = setting.Size.ToString();

    Response.Cookies.Add(cookie);
}

private void ReadCookie_2a()
{
    HttpCookie cookie = Request.Cookies["DisplaySettings1"];
    if( cookie == null )
        labDisplaySettings1.Text = "未定義";
    else {
        DisplaySettings setting = new DisplaySettings();
        setting.Style = cookie["Style"].TryToInt();
        setting.Size = cookie["Size"].TryToInt();
        labDisplaySettings1.Text = setting.ToString();
    }
}

方法-2,將物件JSON序列化為字串。

private void WriteCookie_2b()
{
    DisplaySettings setting = new DisplaySettings { Style = 2, Size = 48 };

    HttpCookie cookie = new HttpCookie("DisplaySettings2", setting.ToJson());
    Response.Cookies.Add(cookie);
}

private void ReadCookie_2b()
{
    HttpCookie cookie = Request.Cookies["DisplaySettings2"];
    if( cookie == null )
        labDisplaySettings2.Text = "未定義";
    else {
        DisplaySettings setting = cookie.Value.FromJson<DisplaySettings>();
        labDisplaySettings2.Text = setting.ToString();
    }
}

這段程式碼使用了我定義的二個擴充套件方法。細說Cookie

對於這二種方法,我個人更喜歡後者,因為它具有更好擴充套件性:如果型別增加了成員,不需要修改讀寫Cookie的程式碼。
不過,這種方式產生的有些字元,比如【雙引號】,極少數瀏覽器(Opera)不支援,所以需要做UrlEncode或者Base64編碼處理。
同理,對於第一種方法,遇到Value有【雙引號】時,我們同樣需要做UrlEncode或者Base64編碼處理。

Js中讀寫Cookie

Cookie並非只能在服務端讀寫,在客戶端的瀏覽器中也可以實現對它的讀寫訪問。而且在JS中建立的Cookie對於服務端仍然有效(可見),接下來我們來看看在JS中如何寫入Cookie,演示程式碼將建立一個按鈕,並在點選按鈕後寫入Cookie

<input type="button" onclick="WriteCookie();" value="WriteCookie" />

<script type="text/javascript">
    function WriteCookie() {
        var cookie = "cookie_js=22222222; path=/";
        document.cookie = cookie;
    }    
</script>

在JS中寫Cookie很簡單,只要給document.cookie賦值一個Cookie字串即可,至於格式,可以參考前面用Fiddle看到的結果。

再來看一下如何使用JS讀取Cookie吧。請參考如下程式碼:

<input type="button" onclick="ReadCookie();" value="ReadCookie" />

<script type="text/javascript">
    function ReadCookie() {
        alert(document.cookie);
    }    
</script>

細說Cookie

仍然是訪問document.cookie,不過,這次我們得到卻是全部的Cookie值,每個Key/Value項用分號分開,中間則用等號分開。 所以,如果您想在JS中讀取Cookie,一定要按照這個規則來拆分並解析您要讀取的Cookie項。鑑於這樣的操作有些繁瑣,我們可以jquery.cookie.js外掛來輕鬆完成這個功能,有興趣的朋友也可以看一下它是如何處理的。這個外掛的程式碼比較少,這裡就直接貼出, 細說Cookie

注意哦:前面我們看到了HttpCookie有個HttpOnly屬性,如果它為true,那麼JS是讀不到那個Cookie的,也就是說:我們如果在服務端生成的Cookie不希望在JS中能被訪問,可以在寫Cookie時,設定這個屬性。不過,通過一些工具,還是可以看到它們。

接下來,我們再來看看Asp.net中Cookie有哪些應用。

Cookie在Session中的應用

在Asp.net中,HttpContext, Page物件都有個Session的物件,我們可以使用它來方便地在服務端儲存一些與會話相關的資訊。
前面我們也提到過,HTTP協議是無狀態的,對於一個瀏覽器發出的多次請求,WEB伺服器無法區分 是不是來源於同一個瀏覽器。 所以,為了實現會話,服務端需要一個會話標識ID能儲存到瀏覽器,讓它在後續的請求時都帶上這個會話標識ID,以便讓服務端知道某個請求屬於哪個會話,這樣便可以維護與會話相關的狀態資料。由於Cookie對於使用者來說,是個不可見的東西,而且每次請求都會傳遞到服務端,所以它就是很理想的會話標識ID的儲存容器。在Asp.net中,預設也就是使用Cookie來儲存這個ID的。注意:雖然Asp.net 2.0 也支援無Cookie的會話,但那種方式要修改URL,也有它的缺點,因此這種方法並沒有廣泛的使用。本文將不對這個話題做過多的分析,就此略過無Cookie會話這種方式。

我們來看看Session是如何使用Cookie來儲存會話標識ID的,在預設的Asp.net配置中,Web.config有著如下定義:

<sessionState mode="InProc" cookieName="ASP.NET_SessionId" cookieless="UseCookies"></sessionState>

如果我們執行以下操作:

Session["Key1"] = DateTime.Now;

此時,我們可以使用一些瀏覽器提供的工具來檢視一下現在的Cookie情況。

細說Cookie

從圖片上看,這個Cookie的名字就是我們在配置檔案中指出的名稱,我們可以修改一下配置檔案:

<sessionState cookieName="SK"></sessionState>

再來執行上面的寫Session的操作,然後看Cookie

細說Cookie

我們可以看到:SK的Cookie出現了。說明:在截圖時我把名稱為"ASP.NET_SessionId"的Cookie刪除了。

通過上面示例,我們可以得到結論,Session的實現是與Cookie有關的,服務端需要將會話標識ID儲存到Cookie中。
這裡再一次申明,除非你使用無Cookie的會話模式,否則Session是需要Cookie的支援。反過來,Cookie並不需要Session的支援。

Cookie在身份驗證中的應用

我想很多人都在Asp.net的開發中使用過Form身份認證。對於一個使用者請求,我們可以在服務端很方便地判斷它是不是代表一個已登入使用者。

this.labStatus.Text = (Request.IsAuthenticated ? "已登入" : "未登入");

那麼,您有沒有好奇過:Asp.net是如何識別一個請求是不是一個已登入使用者發起的呢?說到這裡,我們就要從使用者登入說起了。為了實現登入及Form認證方式,我們需要如下配置:

<authentication mode="Forms" >
    <forms name="UserStatus"></forms>
</authentication>

接下來,我們需要實現使用者登入邏輯。具體實現方式有很多,不過,最終的呼叫都是差不多的,如下程式碼所示:

private void SetLogin()
{
    System.Web.Security.FormsAuthentication.SetAuthCookie("fish", false);
}

只要執行了以上程式碼,我們就可以看到,前面的判斷【Request.IsAuthenticated】返回true,最終會顯示"已登入"。為了探尋這個祕密,我們還是來看一下當前頁面的Cookie情況。

細說Cookie

果然,多出來一個Cookie,名稱與我在配置檔案中指定的名稱相同。我們再來看看如果登出當前登入會是什麼樣子的:

private void SetLogout()
{
    System.Web.Security.FormsAuthentication.SignOut();
}

細說Cookie

看到了嗎,名為"UserStatus"的Cookie不見了。此時如果你再去觀察【Request.IsAuthenticated】,可以發現它此時返回 false。或者,您也可以再試一次,登入後,直接刪除名為"UserStatus"的Cookie,也能發現登入狀態將顯示"未登入"。或許,您還是有點不清楚前面我呼叫【System.Web.Security.FormsAuthentication.SetAuthCookie("fish", false);】後,Asp.net做了些什麼,回答這個問題其實很簡單:自己用Reflector.exe去看一下Asp.net的實現吧。
這裡為了更讓您能信服登入與Cookie有關,我將直接建立一個Cookie看一下 Asp.net能不能認可我建立的Cookie,並認為登入有效。請看程式碼:

private void SetLogin()
{
    //System.Web.Security.FormsAuthentication.SetAuthCookie("fish", false);

    // 下面的程式碼和上面的程式碼在作用上是等效的。
    FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
        2, "fish", DateTime.Now, DateTime.Now.AddDays(30d), false, string.Empty);
    string str = FormsAuthentication.Encrypt(ticket);

    HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, str);
    Response.Cookies.Add(cookie);
}

如果執行這段程式碼,您將發現:【Request.IsAuthenticated】返回true,登入狀態會顯示"已登入"。
至此,我們可以得出一個結論: Form身份認證依賴Cookie,Asp.net就是每次檢查我們在配置檔案中指定的Cookie名稱,並解密這個Cookie來判斷當前請求使用者的登入狀態。

Cookie的安全狀況

從以上圖片,您應該能發現:瀏覽器能提供一些介面讓使用者清楚的觀察我們在服務端寫的Cookie,甚至有些瀏覽器還提供很方便的修改功能。如下圖所示:

細說Cookie

所以,我們在服務端寫程式碼讀取Cookie時,尤其是涉及型別轉換、反序列化或者解密時,一定要注意這些操作都有可能會失敗。而且上圖也清楚的反映了一個事實:Cookie中的值都是“一目瞭然”的,任何人都能看到它們。所以,我們儘量不要直接在Cookie中儲存一些重要的或者敏感的內容。如果我們確實需要使用Cookie儲存一些重要的內容,但又不希望被他人看懂,我們可以使用一些加密的方法來保護這些內容。

1. 對於一些重要性不高的內容,我們可以使用Base64之類的簡單處理方式來處理。

2. 對於重要性相對高一點的內容,我們可以利用.net提供的一些加密工具類,自己來設計加密方法來保護。不過,密碼學與加密解密並不是很簡單的演算法,因此,自己設計的加密方式可能不會很安全。

3. 重要的內容,我們可以使用.net提供的FormsAuthenticationTicket,FormsAuthentication來加密。我認為這種方式還是比較安全的。畢竟前面我們也看過了,Asp.net的Form身份認證就是使用這種方式來加密使用者登入的身份標識的,所以,如果這種方式不安全,也就意味著Asp.net的身份認證也不安全了。如果您使用這種方式來加密,那麼請注意:它產生的加密後文字還是比較大的,前面我也提到過,每次請求時,瀏覽器都會帶上與請求相匹配的所有Cookie,因此,這種Cookie會對傳輸效能產生一定的影響,所以,請小心使用,切記不可過多的使用。

這裡要補充一下:去年曾經出現過【Padding Oracle Attack】這個話題,一些人甚至錯誤的認為是Asp.net加密方式不安全!如果您也是這樣認為的,那麼可以看一下這篇文章: 淺談這次ASP.NET的Padding Oracle Attack相關內容 ,以消除這個錯誤的認識。當然了,我們也可以從這個話題得到一些收穫:解密失敗時,不要給出過多的提示,就當沒有這個Cookie存在。

如何在C#發請的請求中使用Cookie

前面我們一直在談服務端與瀏覽器中使用Cookie,其實瀏覽器也是一個普通的應用程式,.net framework也提供一些類也能讓我們直接發起HTTP請求,下面我們來看一下如何在C#發請的請求中使用Cookie ,其實也很簡單,主要是使用了CookieContainer類,請看以下演示程式碼:

private static string SendHttpRequestGet(string url, Encoding encoding, 
            CookieContainer cookieContainer)
    {
        if( string.IsNullOrEmpty(url) )
            throw new ArgumentNullException("url");

        if( encoding == null )
            throw new ArgumentNullException("encoding");

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "GET";
        request.CookieContainer = cookieContainer;
        
        using( WebResponse response = request.GetResponse() ) {
            using( StreamReader reader = new StreamReader(response.GetResponseStream(), encoding) ) {
                return reader.ReadToEnd();
            }
        }
    }

    private void SendHttpDEMO()
    {
        StringBuilder sb = new StringBuilder();
        CookieContainer cookieContainer = new CookieContainer();

        string url = "http://www.taobao.com";
        SendHttpRequestGet(url, Encoding.Default, cookieContainer);

        // 後面可以繼續發起HTTP請求,此時將會包含上次從伺服器寫入的Cookie
        //SendHttpRequestGet("同域名下的其它URL", Encoding.Default, cookieContainer);

        // 至此,我們可以顯示取得了哪些Cookie
        CookieCollection cookies = cookieContainer.GetCookies(new Uri(url));
        if( cookies != null ) {
            foreach( System.Net.Cookie cookie in cookies )
                sb.AppendLine(cookie.ToString());
        }
        txtCookies.Text = sb.ToString();
    }

重構與使用總結

在前面的Asp.net示例程式碼中,我一直使用.net提供的HttpCookie類來操作Cookie,是為了展示用原始的方式來使用Cookie,這些程式碼有點重複,也有點繁瑣,為此,我提供了幾個簡單的方法可以更容易的使用Cookie,也算是對Cookie使用的一個總結。

/// <summary>
/// 用於方便使用Cookie的擴充套件工具類
/// </summary>
public static class CookieExtension
{
    // 我們可以為一些使用頻率高的型別寫專門的【讀取】方法

    /// <summary>
    /// 從一個Cookie中讀取字串值。
    /// </summary>
    /// <param name="cookie"></param>
    /// <returns></returns>
    public static string GetString(this HttpCookie cookie)
    {
        if( cookie == null )
            return null;

        return cookie.Value;
    }

    /// <summary>
    /// 從一個Cookie中讀取 Int 值。
    /// </summary>
    /// <param name="cookie"></param>
    /// <param name="defaultVal"></param>
    /// <returns></returns>
    public static int ToInt(this HttpCookie cookie, int defaultVal)
    {
        if( cookie == null )
            return defaultVal;

        return cookie.Value.TryToInt(defaultVal);
    }

    /// <summary>
    /// 從一個Cookie中讀取值並轉成指定的型別
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="cookie"></param>
    /// <returns></returns>
    public static T ConverTo<T>(this HttpCookie cookie)
    {
        if( cookie == null )
            return default(T);

        return (T)Convert.ChangeType(cookie.Value, typeof(T));
    }

    /// <summary>
    /// 從一個Cookie中讀取【JSON字串】值並反序列化成一個物件,用於讀取複雜物件
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="cookie"></param>
    /// <returns></returns>
    public static T FromJson<T>(this HttpCookie cookie)
    {
        if( cookie == null )
            return default(T);

        return cookie.Value.FromJson<T>();
    }


    /// <summary>
    /// 將一個物件寫入到Cookie
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="name"></param>
    /// <param name="expries"></param>
    public static void WriteCookie(this object obj, string name, DateTime? expries)
    {
        if( obj == null )
            throw new ArgumentNullException("obj");

        if( string.IsNullOrEmpty(name) )
            throw new ArgumentNullException("name");
        

        HttpCookie cookie = new HttpCookie(name, obj.ToString());

        if( expries.HasValue )
            cookie.Expires = expries.Value;

        HttpContext.Current.Response.Cookies.Add(cookie);
    }

    /// <summary>
    /// 刪除指定的Cookie
    /// </summary>
    /// <param name="name"></param>
    public static void DeleteCookie(string name)
    {
        if( string.IsNullOrEmpty(name) )
            throw new ArgumentNullException("name");

        HttpCookie cookie = new HttpCookie(name);

        // 刪除Cookie,其實就是設定一個【過期的日期】
        cookie.Expires = new DateTime(1900, 1, 1);
        HttpContext.Current.Response.Cookies.Add(cookie);
    }
}

更完整的程式碼可以從本文的示例程式碼中獲得。(文章底部有下載地址)

使用方式:

public static class TestClass
{
    public static void Write()
    {
        string str = "中國";
        int aa = 25;
        DisplaySettings setting = new DisplaySettings { Style = 3, Size = 50 };
        DateTime dt = new DateTime(2012, 1, 1, 12, 0, 0);

        str.WriteCookie("Key1", DateTime.Now.AddDays(1d));
        aa.WriteCookie("Key2", null);
        setting.ToJson().WriteCookie("Key3", null);
        dt.WriteCookie("Key4", null);
    }

    public static void Read()
    {
        HttpRequest request = HttpContext.Current.Request;

        string str = request.Cookies["Key1"].GetString();
        int num = request.Cookies["Key2"].ToInt(0);
        DisplaySettings setting = request.Cookies["Key3"].FromJson<DisplaySettings>();
        DateTime dt = request.Cookies["Key4"].ConverTo<DateTime>();
    }    
}

注意哦:以上程式碼中都是直接使用字串"Key"的形式,這種方式對於大一些的程式在後期可能會影響維護。
所以建議:將訪問Cookie所使用的Key能有一個類來統一的定義,或者將讀寫操作包裝成一些屬性放在一個類中統一的管理。

public static class CookieValues
{
    // 建議把Cookie相關的引數放在一起,提供 get / set 屬性(或者方法)來訪問,以避免"key"到處亂寫

    public static string AAA
    {
        get { return HttpContext.Current.Request.Cookies["Key1"].GetString(); }
    }
    public static int BBB
    {
        get { return HttpContext.Current.Request.Cookies["Key2"].ToInt(0); }
    }
    public static DisplaySettings CCC
    {
        get { return HttpContext.Current.Request.Cookies["Key3"].FromJson<DisplaySettings>(); }
    }
    public static DateTime DDD
    {
        get { return HttpContext.Current.Request.Cookies["Key4"].ConverTo<DateTime>(); }
    }
}

補充

根據一些朋友提供的反饋,這裡再補充4個需要注意的地方:

1. 如果使用Form登入驗證且希望使用Cookie方式時,建議設定 cookieless="UseCookies",因為這個引數的預設值是:cookieless="UseDeviceProfile",Asp.net可能會誤判。 dudu就吃過虧。

<authentication mode="Forms" >
    <forms name="MyCookieName" cookieless="UseCookies"></forms>
</authentication>

2. Cookie有3個屬性,一般我們可以不用設定,但它們的值可以在Web.config中指定預設值:

<httpCookies domain="www.123.com" httpOnlyCookies="true" requireSSL="false"/>

3. 雖然在寫Cookie時,我們可以設定name, value之外的其它屬性,但是在讀取時,是讀不到這些設定的。其實在我的示例程式碼中有體現,我前面也忘記了說明了。

4. HttpRequest.Cookies 與 HttpResponse.Cookies 會有關係(很奇怪吧)。
以下程式碼演示了這個現象:

protected void Page_Load(object sender, EventArgs e)
{
    DateTime.Now.ToString().WriteCookie("t1", null);

    label1.Text = ShowAllCookies();

    Guid.NewGuid().ToString().WriteCookie("t2", null);

    // 如果去掉下面程式碼,將會看到2個t1 
    Response.Cookies.Remove("t1");
    Response.Cookies.Remove("t2");
}

private string ShowAllCookies()
{
    StringBuilder sb = new StringBuilder();

    for( int i = 0; i < Request.Cookies.Count; i++ ) {
        HttpCookie cookie = Request.Cookies[i];
        sb.AppendFormat("{0}={1}<br />", cookie.Name, cookie.Value);
    }

    return sb.ToString();
}

上面的試驗程式碼將會一直顯示 t1 的Cookie ,這裡就不再貼圖了。

本文的所有示例程式碼可以點選此處下載。

相關文章