昨天無聊在園子里看到一篇新聞 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單步調試,查看監視信息。
在代碼執行了前兩行時,在即時窗口進行了以下的測試:
結果讓我有些失望,兩個不是同一個東西。
不灰心,繼續執行,具體每步之后的監視信息這里就不貼了,有興趣的同學自己試下,上結果:我們對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 的集合”解釋不准確的尷尬。