昨天无聊在园子里看到一篇新闻 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 的集合”解释不准确的尴尬。