鑽牛角尖之Request.Cookies與Response.Cookies


 

       昨天無聊在園子里看到一篇新聞 8歲小學生表白遭拒:被一部iPhone打敗 ,看到這樣文章出現在技術園子里(估計就因為一個iphone的關鍵字),並且比同時間的新聞閱讀量高出很多,就發出了程序員有多無聊的感嘆,其實也有自嘲的性質(因為最近確實有點迷惘,無法定下心看一些技術文章,特別長一點的,看到后往往收藏或mark了事,安慰自己以后看,其實很少再去看了),果然遭到了園子里同學的無情嘲諷,哈哈,無聊的程序員是多。。。

      除此之外,也做了點“正事”,看了園子里人氣很高的 fish li 的 我心目中的Asp.net核心對象 ,實話說,畢竟也工作了幾年,對文章里提到一些東西還是了解的,但是在看到文章中提到:

HttpRequest有一個Cookies屬性,MSDN給它的解釋是:“獲取客戶端發送的 Cookie 的集合。”,這次MSDN的解釋就不完全准確了。

然后貼了一個例子:

protected void Page_Load(object sender, EventArgs e)
{
    string key = "Key1";

    HttpCookie c = new HttpCookie(key, DateTime.Now.ToString());
    Response.Cookies.Add(c);


    HttpCookie cookie = Request.Cookies[key];
    if( cookie != null )
        this.labResult.Text = cookie.Value;


    Response.Cookies.Remove(key);
}

這個例子說明:Request.Cookies不僅來自於客戶端發來的cookie集合,還會受到Response.Cookies修改的影響。

以前這塊還真沒有仔細了解過,認為這兩個一個就是獲取客戶端傳來的cookie的集合,一個就是我們想輸出到客戶端的cookie的集合,雖然都是cookie,應該不相干的,但是竟然出現了這樣的關聯,這給我的一個顛覆:這兩個集合引用的是同一個實例?!

 

Request.Cookies、Response.Cookies是同一個人的兩個身份嗎

為什么這樣假設,因為它能很好的解釋上面的結果,但是實際是這樣嗎?我也做了實驗:

protected void Page_Load(object sender, EventArgs e)
{
    HttpCookieCollection hccRequest = Request.Cookies;
    HttpCookieCollection hccResponse = Response.Cookies;


    string key = "Key1";
    HttpCookie c = new HttpCookie(key, "1");
    hccResponse.Add(c);
    hccResponse.Remove(key);


    String key2 = "key2";
    HttpCookie cookie = new HttpCookie(key2,"2");
    hccRequest.Add(cookie);

}

 

代碼比較簡單,然后F5,F10單步調試,查看監視信息。

在代碼執行了前兩行時,在即時窗口進行了以下的測試:

image

結果讓我有些失望,兩個不是同一個東西。

不灰心,繼續執行,具體每步之后的監視信息這里就不貼了,有興趣的同學自己試下,上結果:我們對Response.Cookies添加、刪除都會及時的反映到Request.Cookies,但是對Request.Cookies的添加操作時,Response.Cookies沒有反應。

這樣結論就有了:兩者是兩個實例,但為什么Response.Cookies會影響Request.Cookies?!

 

為什么Response.Cookies會影響Request.Cookies

再往下,就要借助.net reflector。

先看一下,Request.Cookies的代碼:

public HttpCookieCollection Cookies
{
    get
    {
        if (this._cookies == null)
        {
            this._cookies = new HttpCookieCollection(null, false);
            if (this._wr != null)
            {
                this.FillInCookiesCollection(this._cookies, true);
            }
        }
        if (this._flags[4])
        {
            this._flags.Clear(4);
            this.ValidateCookieCollection(this._cookies);
        }
        return this._cookies;
    }
}

 

在HttpRequest內部,對應_cookies的私有變量,再深入討論這段代碼之前,我們還是先看一下Response.Cookies的代碼:

public HttpCookieCollection Cookies
{
    get
    {
        if (this._cookies == null)
        {
            this._cookies = new HttpCookieCollection(this, false);
        }
        return this._cookies;
    }
}

 

在HttpResponse內部,也是對應一個_cookies的私有變量,不過從這兩段代碼,我們更確定了它們不是同一個人,它們都通過new HttpCookieCollection來初始化了自己,創建了一個新的實例。

接下來看一下它們實例化的構造函數:

internal HttpCookieCollection(HttpResponse response, bool readOnly) : base(StringComparer.OrdinalIgnoreCase)
{
    this._response = response;
    base.IsReadOnly = readOnly;
}

 

關鍵在第一個參數,它要求是一個HttpResponse的實例,Response.Cookies實例化時傳入自己的Response是應該的,但是Request.Cookies卻傳入的是null。

 

為什么說它是關鍵,接下來看下HttpCookieCollection的Add操作:

public void Add(HttpCookie cookie)
{
    if (this._response != null)
    {
        this._response.BeforeCookieCollectionChange();
    }
    this.AddCookie(cookie, true);
    if (this._response != null)
    {
        this._response.OnCookieAdd(cookie);
    }
}

這時我們可以確定一些事情了,由於Request.Cookies傳入null,這段代碼對於它來說,只是執行了AddCookie,實際的代碼就只是添加了一個cookie項;而對於Response.Cookies,還要執行BeforeCookieCollectionChange和OnCookieAdd的操作,重點在OnCookieAdd操作:

internal void OnCookieAdd(HttpCookie cookie)
{
    this.Request.AddResponseCookie(cookie);
}

 

竟然去執行了Request的AddResponseCookie操作:

internal void AddResponseCookie(HttpCookie cookie)
{
    if (this._cookies != null)
    {
        this._cookies.AddCookie(cookie, true);
    }
    if (this._params != null)
    {
        this._params.MakeReadWrite();
        this._params.Add(cookie.Name, cookie.Value);
        this._params.MakeReadOnly();
    }
}

 

而這個操作里又對_cookies進行了修改,從上邊我們知道Request.Cookies內部引用的就是它,因此從這里就從代碼上解釋了:我們對Response.Cookies添加、刪除都會及時的反映到Request.Cookies,但是對Request.Cookies的添加操作時,Response.Cookies沒有反應。

 

另外,我們在Request.Cookies的代碼里看到有FillInCookiesCollection(this._cookies, true)的操作,代碼太長,只貼下它的聲明:

internal void FillInCookiesCollection(HttpCookieCollection cookieCollection, bool includeResponse)

 

這個操作就是從客戶端傳來的cookie信息中去填充Request.Cookies集合,看第二個參數bool includeResponse,是否包含response,傳入的是true說這個填充,不只是填充了Request.Cookies集合,也填充了Response.Cookies集合,這也就解釋了: 我們每次拿到的未經操作的這兩個集合總是包含同樣的值,盡管是兩個不同的實例。

 

更正:實在對不起,自己后來做個實驗發現,填充Request.Cookies集合,並沒有填充了Response.Cookies集合。貼上一段FillInCookiesCollection用到includeResponse的代碼:

if (includeResponse && (this.Response != null))
        {
            HttpCookieCollection cookies = this.Response.Cookies;
            if (cookies.Count > 0)
            {
                HttpCookie[] dest = new HttpCookie[cookies.Count];
                cookies.CopyTo(dest, 0);
                for (int i = 0; i < dest.Length; i++)
                {
                    cookieCollection.AddCookie(dest[i], true);
                }
            }
        }
其實includeResponse的作用是填充Request.Cookies集合時是否把Response.Cookies集合中的數據也填充到Request.Cookies集合里。這段代碼的作用就是把 Response.Cookies集合中的數據也填充到Request.Cookies集合。這樣:Response.Cookies每次都是空的,除非你主動操作了,如果操作,就去修改之前的cookie,沒有就保持原來的cookie不變。

 

結論:Request.Cookies,Response.Cookies是兩個不同的實例,兩個集合實例的添加、刪除操作就不同了,對Response.Cookies添加、刪除都會及時的反映到Request.Cookies,對Request.Cookies的添加、刪除操作,Response.Cookies沒有反應(但是因為相同的鍵對應的值是引用類型,對這些值進行的修改會相互影響)。

 

疑惑

      雖然了解了事情的真相,但是有了更多的疑惑:為什么要這樣設計?為什么之后還要同步Response.Cookies的添加刪除到Request.Cookies呢,有啥意義呢,要輸出的cookie竟然影響了請求傳送的cookie集合,反而產生了“Request.Cookies是獲取客戶端發送的 Cookie 的集合”解釋不准確的尷尬。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM