Asp.net cookie的处理流程你真的知道吗?

一说到Cookie我想大家都应该知道它是一个保存在客户端,当浏览器请求一个url时,浏览器会携带相关的Cookie达到服务器端,所以服务器 是可以操作Cookie的,在Response时,会把Cookie信息输出到客服端。下面我们来看一个demo吧,代码如下:

第一次请求结果如下:

第二次请求结果如下:

到这里我们可以看到第二次请求传入的Cookie正好是第一次请求返回的Cookie信息,这里的cookie信息的维护主要是我们客户端的浏览 器,但是在Asp.net程序开发时,Cookie往往是在服务端程序里面写入,就如我的事例代码;很少有用客服端js实现的。现在我们就来看看 asp.net服务端是如何实现读写Cookie的。

首先我们来看看HttpRequest的Cookie是如何定义的:

        public HttpCookieCollection Cookies {
            get {
                EnsureCookies();
                if (_flags[needToValidateCookies]) {
                    _flags.Clear(needToValidateCookies);
                    ValidateCookieCollection(_cookies);
                }
                return _cookies;
            }
        }
 这里的Cookie获取主要是调用一个EnsureCookies方法,EnsureCookies放主要是调用FillInCookiesCollection方法,其中Cookie属性返回的是一个HttpCookieCollection集合,
        // Populates the Cookies property but does not hook up validation.
        internal HttpCookieCollection EnsureCookies() {
            if (_cookies == null) {
                _cookies = new HttpCookieCollection(null, false);
                if (_wr != null)
                    FillInCookiesCollection(_cookies, true /*includeResponse*/);

                if (HasTransitionedToWebSocketRequest) // cookies can't be modified after the WebSocket handshake is complete
                    _cookies.MakeReadOnly();
            }
            return _cookies;
        }

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

 其中这里的FillInCookiesCollection方法实现也比较复杂:

 说简单一点它主要调用HttpWorkerRequest的GetKnownRequestHeader方法获取浏览器传进来的Cookie字符串信息,然后再把这些信息根据;来分隔成多个HttpCookie实例。把这些HttpCookie实例添加到传进来的HttpCookieCollection参数。

这里HttpWorkerRequest继承结果如下:

internal class ISAPIWorkerRequestInProcForIIS7 : ISAPIWorkerRequestInProcForIIS6
internal class ISAPIWorkerRequestInProcForIIS6 : ISAPIWorkerRequestInProc
internal class ISAPIWorkerRequestInProc : ISAPIWorkerRequest
internal abstract class ISAPIWorkerRequest : HttpWorkerRequest

其中 GetKnownRequestHeader方法的实现主要是在ISAPIWorkerRequest中,其GetKnownRequestHeader 主要是调用了它的ReadRequestHeaders私有方法,在ReadRequestHeaders方法中主要是调用它的 this.GetServerVariable("ALL_RAW")方法,所以我们可以认为this.GetServerVariable("ALL_RAW")这个方法是获取客户端传来的Cookie参数,而GetServerVariable方法的实现主要是在ISAPIWorkerRequestInProc 类,具体实现非常复杂。

这里的GetKnownRequestHeader方法实现非常复杂我们也就不去深研它了,我们只要知道调用这个方法就会返回Cookie的所有字 符串信息。在这个方法里面还调用了一个CreateCookieFromString方法,根据字符串来创建我们的HttpCookie实例。 CreateCookieFromString方法实现如下:

 我们平时很少用到HttpCookie的Values属性,所以这个属性大家还是需要注意一下,这个方法就是把一个cookie的字符串转化为相应的HttpCookie实例。
现在我们回到HttpRequest的Cookies属性中来,这里有一个关于Cookie的简单验证ValidateCookieCollection方法,

 private void ValidateCookieCollection(HttpCookieCollection cc) {
            if (_enableGranularValidation) {
                // Granular request validation is enabled - validate collection entries only as they're accessed.
                cc.EnableGranularValidation((key, value) => ValidateString(value, key, RequestValidationSource.Cookies));
            }
            else {
                // Granular request validation is disabled - eagerly validate all collection entries.
                int c = cc.Count;
 
                for (int i = 0; i < c; i++) {
                    String key = cc.GetKey(i);
                    String val = cc.Get(i).Value;

                    if (!String.IsNullOrEmpty(val))
                        ValidateString(val, key, RequestValidationSource.Cookies);
                }
            }
        }

其中HttpCookieCollection的EnableGranularValidation实现如下:

 internal void EnableGranularValidation(ValidateStringCallback validationCallback)
    {
        this._keysAwaitingValidation = new HashSet<string>(this.Keys.Cast<string>(), StringComparer.OrdinalIgnoreCase);
        this._validationCallback = validationCallback;
    }

    private void EnsureKeyValidated(string key, string value)
    {
        if ((this._keysAwaitingValidation != null) && this._keysAwaitingValidation.Contains(key))
        {
            if (!string.IsNullOrEmpty(value))
            {
                this._validationCallback(key, value);
            }
            this._keysAwaitingValidation.Remove(key);
        }
    }

到这里我们知道默认从浏览器发送到服务器端的Cookie都是需要经过次验证的。这里的ValidateString方法具体实现我们就不说了,不过大家需要知道它是调用了RequestValidator.Current.IsValidRequestString方法来实现验证的,有关RequestValidator的信息大家可以查看HttpRequest的QueryString属性 的一点认识 。现在我们获取Cookie已经基本完成了。那么我们接下来看看是如何添加Cookie的了。

首先我们来看看HttpResponse的Cookie属性:

public HttpCookieCollection Cookies
{
    get
    {
        if (this._cookies == null)
        {
            this._cookies = new HttpCookieCollection(this, false);
        }
        return this._cookies;
    }
}
接下来我们看看HttpCookie的实现如下:

 现在我们回到HttpCookieCollection的Add方法看看,

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

            if (_response != null)
                _response.OnCookieAdd(cookie);
        }
 
public sealed class HttpResponse
{
  internal void BeforeCookieCollectionChange()
   {
    if (this._headersWritten)
    {
        throw new HttpException(SR.GetString("Cannot_modify_cookies_after_headers_sent"));
    }

  }
 internal void OnCookieAdd(HttpCookie cookie)
 {
    this.Request.AddResponseCookie(cookie);
 }
}
public sealed class HttpRequest
{
  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();
    }
 }
}

到这里我们应该知道每添加或修改一个Cookie都会调用HttpResponse的BeforeCookieCollectionChangeOnCookieAdd方法,BeforeCookieCollectionChange是确认我们的cookie是否可以添加的,以前在项目中就遇到这里的错误信息说什么“在header发送后不能修改cookie”,看见默认情况下_headersWritten是false,那么它通常在哪里被设置为true了,在HttpReaponse的BeginExecuteUrlForEntireResponse、Flush、EndFlush方法中被设置为true,而我们最常接触到的还是Flush方法。这里的OnCookieAdd方法确保Cookie实例同时也添加到HttpRequest中。

  internal void AddCookie(HttpCookie cookie, bool append) {
            ThrowIfMaxHttpCollectionKeysExceeded();
 
            _all = null;
            _allKeys = null;

            if (append) {
                // DevID 251951    Cookie is getting duplicated by ASP.NET when they are added via a native module
                // Need to not double add response cookies from native modules
                if (!cookie.FromHeader) {
                    // mark cookie as new
                    cookie.Added = true;
                }
                BaseAdd(cookie.Name, cookie);
            }
            else {
                if (BaseGet(cookie.Name) != null) {
                    // mark the cookie as changed because we are overriding the existing one
                    cookie.Changed = true;
                }
                BaseSet(cookie.Name, cookie);
            }
        }
         private void ThrowIfMaxHttpCollectionKeysExceeded() {
            if (Count >= AppSettings.MaxHttpCollectionKeys) {
                throw new InvalidOperationException(SR.GetString(SR.CollectionCountExceeded_HttpValueCollection, AppSettings.MaxHttpCollectionKeys));
            }

        }

这里的AddCookie方法也非常简单,不过每次添加都会去检查Cookie的个数是否超过最大值。其实添加Cookie还可以调用HttpResponse的AppendCookie方法,

public void AppendCookie(HttpCookie cookie)
{
    if (this._headersWritten)
    {
        throw new HttpException(SR.GetString("Cannot_append_cookie_after_headers_sent"));
    }
    this.Cookies.AddCookie(cookie, true);
    this.OnCookieAdd(cookie);
}
这里它的实现和HttpCookieCollection的     public void Add(HttpCookie cookie)方法实现一致。
 同样我们也知道这些Cookie是在HttpResponse的GenerateResponseHeadersForCookies方法中被使用,
其中GenerateResponseHeadersForCookies方法的实现如下:

 

 这里我们还是来总结一下吧:在HttpWorkerRequest中我们调用 GetKnownRequestHeader方法来获取Cookie的字符串形式,然后再将这里的字符串转化为HttpCookie集合供 HttpRequest使用,在HttpResponse中的GenerateResponseHeadersForCookies方法中会处理我们的 cookie实例,调用cookie的GetSetCookieHeader方法得到HttpCookie对应的字符串值,然后把该值添加到 HttpHeaderCollection 集合中(或者修改已有的值)。在获取cookie是这里有一个验证需要我们注意的就是 RequestValidator.Current.IsValidRequestString方法。   在添加或修改Cookie是有2个地方的检查(1)检查Cookie的个数是否达到我们配置的cookie最大个数,(2)现在是否已经写入头信息,如果 头信息已经写了则不能操作cookie。

posted on   dz45693  阅读(4012)  评论(2编辑  收藏  举报

编辑推荐:
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器

导航

< 2012年12月 >
25 26 27 28 29 30 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示