• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    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放主要是调用
    复制代码 代码如下:

    // 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=v1k2=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=val2key2=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的简单验证
    复制代码 代码如下:

    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 HashSetstring>(this.Keys.Caststring>(), 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的BeforeCookieCollectionChange和OnCookieAdd方法,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。

    您可能感兴趣的文章:
    • asp.net 操作cookie的简单实例
    • ASP.NET之Response.Cookies.Remove 无法删除COOKIE的原因
    • ASP.NET笔记之页面跳转、调试、form表单、viewstate、cookie的使用说明
    • asp.net中使用cookie与md5加密实现记住密码功能的实现代码
    • asp.net Cookie值中文乱码问题解决方法
    • Asp.net内置对象之Cookies(简介/属性方法/基本操作及实例)
    • asp.net关于Cookie跨域(域名)的问题
    • asp.net中的cookie使用介绍
    • ASP.NET下对cookies的操作实现代码
    • asp.net(C#)跨域及跨域写Cookie问题
    • ASP.NET获取不到JS设置cookies的解决方法
    • asp.net页面状态管理cookie和服务器状态管理Session
    • asp.net下cookies操作完美代码
    • ASP.NET与ASP互通COOKIES的一点经验
    • Asp.net 基于Cookie简易的权限判断
    • asp.net各种cookie代码和解析实例
    上一篇:ASP.NET向Javascript传递变量两种实现方法
    下一篇:asp.net 动态创建TextBox控件及状态数据如何加载
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    Asp.net cookie的处理流程深入分析 Asp.net,cookie,的,处理,流程,