• 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方法实现也比较复杂:

      internal void FillInCookiesCollection(HttpCookieCollection cookieCollection, bool includeResponse) {
                if (_wr == null) 
                    return;
    
                String s = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderCookie);
     
                // Parse the cookie server variable.
                // Format: c1=k1=v1&k2=v2; c2=... 
     
                int l = (s != null) ? s.Length : 0;
                int i = 0; 
                int j;
                char ch;
    
                HttpCookie lastCookie = null; 
    
                while (i < l) { 
                    // find next ';' (don't look to ',' as per 91884) 
                    j = i;
                    while (j < l) { 
                        ch = s[j];
                        if (ch == ';')
                            break;
                        j++; 
                    }
     
                    // create cookie form string 
                    String cookieString = s.Substring(i, j-i).Trim();
                    i = j+1; // next cookie start 
    
                    if (cookieString.Length == 0)
                        continue;
     
                    HttpCookie cookie = CreateCookieFromString(cookieString);
     
                    // some cookies starting with '$' are really attributes of the last cookie 
                    if (lastCookie != null) {
                        String name = cookie.Name; 
    
                        // add known attribute to the last cookie (if any)
                        if (name != null && name.Length > 0 && name[0] == '$') {
                            if (StringUtil.EqualsIgnoreCase(name, "$Path")) 
                                lastCookie.Path = cookie.Value;
                            else if (StringUtil.EqualsIgnoreCase(name, "$Domain")) 
                                lastCookie.Domain = cookie.Value; 
    
                            continue; 
                        }
                    }
    
                    // regular cookie 
                    cookieCollection.AddCookie(cookie, true);
                    lastCookie = cookie; 
     
                    // goto next cookie
                } 
    
                // Append response cookies
                if (includeResponse) {
                    // If we have a reference to the response cookies collection, use it directly 
                    // rather than going through the Response object (which might not be available, e.g.
                    // if we have already transitioned to a WebSockets request). 
                    HttpCookieCollection storedResponseCookies = _storedResponseCookies; 
                    if (storedResponseCookies == null && !HasTransitionedToWebSocketRequest && Response != null) {
                        storedResponseCookies = Response.GetCookiesNoCreate(); 
                    }
    
                    if (storedResponseCookies != null && storedResponseCookies.Count > 0) {
                        HttpCookie[] responseCookieArray = new HttpCookie[storedResponseCookies.Count]; 
                        storedResponseCookies.CopyTo(responseCookieArray, 0);
                        for (int iCookie = 0; iCookie < responseCookieArray.Length; iCookie++) 
                            cookieCollection.AddCookie(responseCookieArray[iCookie], append: true); 
                    }
     
                    // release any stored reference to the response cookie collection
                    _storedResponseCookies = null;
                }
            } 
    

     说简单一点它主要调用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方法实现如下:

      internal static HttpCookie CreateCookieFromString(String s) { 
                HttpCookie c = new HttpCookie();
     
                int l = (s != null) ? s.Length : 0; 
                int i = 0;
                int ai, ei; 
                bool firstValue = true;
                int numValues = 1;
    
                // Format: cookiename[=key1=val2&key2=val2&...] 
    
                while (i < l) { 
                    //  find next & 
                    ai = s.IndexOf('&', i);
                    if (ai < 0) 
                        ai = l;
    
                    // first value might contain cookie name before =
                    if (firstValue) { 
                        ei = s.IndexOf('=', i);
     
                        if (ei >= 0 && ei < ai) { 
                            c.Name = s.Substring(i, ei-i);
                            i = ei+1; 
                        }
                        else if (ai == l) {
                            // the whole cookie is just a name
                            c.Name = s; 
                            break;
                        } 
     
                        firstValue = false;
                    } 
    
                    // find '='
                    ei = s.IndexOf('=', i);
     
                    if (ei < 0 && ai == l && numValues == 0) {
                        // simple cookie with simple value 
                        c.Value = s.Substring(i, l-i); 
                    }
                    else if (ei >= 0 && ei < ai) { 
                        // key=value
                        c.Values.Add(s.Substring(i, ei-i), s.Substring(ei+1, ai-ei-1));
                        numValues++;
                    } 
                    else {
                        // value without key 
                        c.Values.Add(null, s.Substring(i, ai-i)); 
                        numValues++;
                    } 
    
                    i = ai+1;
                }
     
                return c;
            } 
    

     我们平时很少用到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的实现如下:

      public sealed class HttpCookie {
            private String _name;
            private String _path = "/";
            private bool _secure; 
            private bool _httpOnly;
            private String _domain; 
            private bool _expirationSet; 
            private DateTime _expires;
            private String _stringValue; 
            private HttpValueCollection _multiValue;
            private bool _changed;
            private bool _added;
     
            internal HttpCookie() {
                _changed = true; 
            } 
    
            /* 
             * Constructor - empty cookie with name
             */
    
            /// <devdoc> 
            ///    <para>
            ///       Initializes a new instance of the <see cref='System.Web.HttpCookie'/> 
            ///       class. 
            ///    </para>
            /// </devdoc> 
            public HttpCookie(String name) {
                _name = name;
    
                SetDefaultsFromConfig(); 
                _changed = true;
            } 
     
            /*
             * Constructor - cookie with name and value 
             */
    
            /// <devdoc>
            ///    <para> 
            ///       Initializes a new instance of the <see cref='System.Web.HttpCookie'/>
            ///       class. 
            ///    </para> 
            /// </devdoc>
            public HttpCookie(String name, String value) { 
                _name = name;
                _stringValue = value;
    
                SetDefaultsFromConfig(); 
                _changed = true;
            } 
     
            private void SetDefaultsFromConfig() {
                HttpCookiesSection config = RuntimeConfig.GetConfig().HttpCookies; 
                _secure = config.RequireSSL;
                _httpOnly = config.HttpOnlyCookies;
    
                if (config.Domain != null && config.Domain.Length > 0) 
                    _domain = config.Domain;
            } 
     
            /*
             * Whether the cookie contents have changed 
             */
            internal bool Changed {
                get { return _changed; }
                set { _changed = value; } 
            }
     
            /* 
             * Whether the cookie has been added
             */ 
            internal bool Added {
                get { return _added; }
                set { _added = value; }
            } 
    
            // DevID 251951	Cookie is getting duplicated by ASP.NET when they are added via a native module 
            // This flag is used to remember that this cookie came from an IIS Set-Header flag, 
            // so we don't duplicate it and send it back to IIS
            internal bool FromHeader { 
                get;
                set;
            }
     
            /*
             * Cookie name 
             */ 
    
            /// <devdoc> 
            ///    <para>
            ///       Gets
            ///       or sets the name of cookie.
            ///    </para> 
            /// </devdoc>
            public String Name { 
                get { return _name;} 
                set {
                    _name = value; 
                    _changed = true;
                }
            }
     
            /*
             * Cookie path 
             */ 
    
            /// <devdoc> 
            ///    <para>
            ///       Gets or sets the URL prefix to transmit with the
            ///       current cookie.
            ///    </para> 
            /// </devdoc>
            public String Path { 
                get { return _path;} 
                set {
                    _path = value; 
                    _changed = true;
                }
            }
     
            /*
             * 'Secure' flag 
             */ 
    
            /// <devdoc> 
            ///    <para>
            ///       Indicates whether the cookie should be transmitted only over HTTPS.
            ///    </para>
            /// </devdoc> 
            public bool Secure {
                get { return _secure;} 
                set { 
                    _secure = value;
                    _changed = true; 
                }
            }
    
            /// <summary> 
            /// Determines whether this cookie is allowed to participate in output caching.
            /// </summary> 
            /// <remarks> 
            /// If a given HttpResponse contains one or more outbound cookies with Shareable = false (the default value),
            /// output caching will be suppressed for that response. This prevents cookies that contain potentially 
            /// sensitive information, e.g. FormsAuth cookies, from being cached in the response and sent to multiple
            /// clients. If a developer wants to allow a response containing cookies to be cached, he should configure
            /// caching as normal for the response, e.g. via the OutputCache directive, MVC's [OutputCache] attribute,
            /// etc., and he should make sure that all outbound cookies are marked Shareable = true. 
            /// </remarks>
            public bool Shareable { 
                get; 
                set; // don't need to set _changed flag since Set-Cookie header isn't affected by value of Shareable
            } 
    
            /// <devdoc>
            ///    <para>
            ///       Indicates whether the cookie should have HttpOnly attribute 
            ///    </para>
            /// </devdoc> 
            public bool HttpOnly { 
                get { return _httpOnly;}
                set { 
                    _httpOnly = value;
                    _changed = true;
                }
            } 
    
            /* 
             * Cookie domain 
             */
     
            /// <devdoc>
            ///    <para>
            ///       Restricts domain cookie is to be used with.
            ///    </para> 
            /// </devdoc>
            public String Domain { 
                get { return _domain;} 
                set {
                    _domain = value; 
                    _changed = true;
                }
            }
     
            /*
             * Cookie expiration 
             */ 
    
            /// <devdoc> 
            ///    <para>
            ///       Expiration time for cookie (in minutes).
            ///    </para>
            /// </devdoc> 
            public DateTime Expires {
                get { 
                    return(_expirationSet ? _expires : DateTime.MinValue); 
                }
     
                set {
                    _expires = value;
                    _expirationSet = true;
                    _changed = true; 
                }
            } 
     
            /*
             * Cookie value as string 
             */
    
            /// <devdoc>
            ///    <para> 
            ///       Gets
            ///       or 
            ///       sets an individual cookie value. 
            ///    </para>
            /// </devdoc> 
            public String Value {
                get {
                    if (_multiValue != null)
                        return _multiValue.ToString(false); 
                    else
                        return _stringValue; 
                } 
    
                set { 
                    if (_multiValue != null) {
                        // reset multivalue collection to contain
                        // single keyless value
                        _multiValue.Reset(); 
                        _multiValue.Add(null, value);
                    } 
                    else { 
                        // remember as string
                        _stringValue = value; 
                    }
                    _changed = true;
                }
            } 
    
            /* 
             * Checks is cookie has sub-keys 
             */
     
            /// <devdoc>
            ///    <para>Gets a
            ///       value indicating whether the cookie has sub-keys.</para>
            /// </devdoc> 
            public bool HasKeys {
                get { return Values.HasKeys();} 
            } 
    
            private bool SupportsHttpOnly(HttpContext context) { 
                if (context != null && context.Request != null) {
                    HttpBrowserCapabilities browser = context.Request.Browser;
                    return (browser != null && (browser.Type != "IE5" || browser.Platform != "MacPPC"));
                } 
                return false;
            } 
     
            /*
             * Cookie values as multivalue collection 
             */
    
            /// <devdoc>
            ///    <para>Gets individual key:value pairs within a single cookie object.</para> 
            /// </devdoc>
            public NameValueCollection Values { 
                get { 
                    if (_multiValue == null) {
                        // create collection on demand 
                        _multiValue = new HttpValueCollection();
    
                        // convert existing string value into multivalue
                        if (_stringValue != null) { 
                            if (_stringValue.IndexOf('&') >= 0 || _stringValue.IndexOf('=') >= 0)
                                _multiValue.FillFromString(_stringValue); 
                            else 
                                _multiValue.Add(null, _stringValue);
     
                            _stringValue = null;
                        }
                    }
     
                    _changed = true;
     
                    return _multiValue; 
                }
            } 
    
            /*
             * Default indexed property -- lookup the multivalue collection
             */ 
    
            /// <devdoc> 
            ///    <para> 
            ///       Shortcut for HttpCookie$Values[key]. Required for ASP compatibility.
            ///    </para> 
            /// </devdoc>
            public String this[String key]
            {
                get { 
                    return Values[key];
                } 
     
                set {
                    Values[key] = value; 
                    _changed = true;
                }
            }
     
            /*
             * Construct set-cookie header 
             */ 
            internal HttpResponseHeader GetSetCookieHeader(HttpContext context) {
                StringBuilder s = new StringBuilder(); 
    
                // cookiename=
                if (!String.IsNullOrEmpty(_name)) {
                    s.Append(_name); 
                    s.Append('=');
                } 
     
                // key=value&...
                if (_multiValue != null) 
                    s.Append(_multiValue.ToString(false));
                else if (_stringValue != null)
                    s.Append(_stringValue);
     
                // domain
                if (!String.IsNullOrEmpty(_domain)) { 
                    s.Append("; domain="); 
                    s.Append(_domain);
                } 
    
                // expiration
                if (_expirationSet && _expires != DateTime.MinValue) {
                    s.Append("; expires="); 
                    s.Append(HttpUtility.FormatHttpCookieDateTime(_expires));
                } 
     
                // path
                if (!String.IsNullOrEmpty(_path)) { 
                    s.Append("; path=");
                    s.Append(_path);
                }
     
                // secure
                if (_secure) 
                    s.Append("; secure"); 
    
                // httponly, Note: IE5 on the Mac doesn't support this 
                if (_httpOnly && SupportsHttpOnly(context)) {
                    s.Append("; HttpOnly");
                }
     
                // return as HttpResponseHeader
                return new HttpResponseHeader(HttpWorkerRequest.HeaderSetCookie, s.ToString()); 
            } 
        }
    

     现在我们回到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方法的实现如下:

    internal void GenerateResponseHeadersForCookies()
            { 
                if (_cookies == null || (_cookies.Count == 0 && !_cookies.Changed)) 
                    return; // no cookies exist
     
                HttpHeaderCollection headers = Headers as HttpHeaderCollection;
                HttpResponseHeader cookieHeader = null;
                HttpCookie cookie = null;
                bool needToReset = false; 
    
                // Go through all cookies, and check whether any have been added 
                // or changed.  If a cookie was added, we can simply generate a new 
                // set cookie header for it.  If the cookie collection has been
                // changed (cleared or cookies removed), or an existing cookie was 
                // changed, we have to regenerate all Set-Cookie headers due to an IIS
                // limitation that prevents us from being able to delete specific
                // Set-Cookie headers for items that changed.
                if (!_cookies.Changed) 
                {
                    for(int c = 0; c < _cookies.Count; c++) 
                    { 
                        cookie = _cookies[c];
                        if (cookie.Added) { 
                            // if a cookie was added, we generate a Set-Cookie header for it
                            cookieHeader = cookie.GetSetCookieHeader(_context);
                            headers.SetHeader(cookieHeader.Name, cookieHeader.Value, false);
                            cookie.Added = false; 
                            cookie.Changed = false;
                        } 
                        else if (cookie.Changed) { 
                            // if a cookie has changed, we need to clear all cookie
                            // headers and re-write them all since we cant delete 
                            // specific existing cookies
                            needToReset = true;
                            break;
                        } 
                    }
                } 
     
    
                if (_cookies.Changed || needToReset) 
                {
                    // delete all set cookie headers
                    headers.Remove("Set-Cookie");
     
                    // write all the cookies again
                    for(int c = 0; c < _cookies.Count; c++) 
                    { 
                        // generate a Set-Cookie header for each cookie
                        cookie = _cookies[c]; 
                        cookieHeader = cookie.GetSetCookieHeader(_context);
                        headers.SetHeader(cookieHeader.Name, cookieHeader.Value, false);
                        cookie.Added = false;
                        cookie.Changed = false; 
                    }
     
                    _cookies.Changed = false; 
                }
            } 
    

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

  • 相关阅读:
    不做男女朋友,做蓝颜吧
    前端 让我尴尬~
    让IE6,IE7,IE8,IE9支持CSS3
    解决chrome不能显示12PX以下字号的字体问题!
    jQuery阻止冒泡和HTML默认操作
    Lazy Load, 延迟加载图片
    解秘微软雅黑
    FLV流媒体应用大攻略
    提升你设计水平的CSS3新技术
    分享下自写的HTML CSS规范
  • 原文地址:https://www.cnblogs.com/majiang/p/2822248.html
Copyright © 2020-2023  润新知