钻牛角尖之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 的集合”解释不准确的尴尬。

posted @ 2012-12-04 13:46  for certain  阅读(16813)  评论(7编辑  收藏  举报