Skip to content

.NET Request.Cookies & Response.Cookies

🏷️ ASP.NET

记录一下最近在调查禁用 .NET 自带的 Session 机制,但是仍然使用 cookie 保存一个类似 SessionId 的值时遇到了以前一直不曾注意的问题。

  1. 启用 .NET 默认的 Session 的情况下,并不是第一次请求就会创建 SessionId ,而是在第一次使用 Session 保存数据时才会生成一个 SessionId 保存到客户端的 cookie。

  2. 调用 Response.Cookies.Clear() 方法并不会清除客户端的 cookie。

    这是在现在项目中发现的遗留 bug。正确的方法是获取 Request.Cookies 中每个 cookie,设置过期时间为之前的日期之后再新增到 Response.Cookies

    示例代码:

    csharp
    var request = HttpContext.Current.Request;
    var response = HttpContext.Current.Response;
    List<string> keys = new List<string>();
    for (int i = 0; i < request.Cookies.Keys.Count; i++)
    {
        keys.Add(request.Cookies.Keys[i]);
    }
    foreach (var key in keys)
    {
        HttpCookie httpCookie = request.Cookies[key];
        httpCookie.Expires = System.DateTime.Now.AddDays(-1);
        response.Cookies.Add(httpCookie);
    }
  3. 通过 Response.Cookies.Add() 方法新增 cookie 到客户端

    可以参考 HttpResponse.Cookies PropertyHttpRequest.Cookies Property 中的注解。

    ASP.NET 包括两个内部 cookie 集合。通过访问的集合 Cookies 的集合 HttpRequest 包含传输中的服务器到客户端的 cookie Cookie 标头。通过访问的集合 Cookies 的集合 HttpResponse 包含在服务器上创建和传输到客户端中的新 cookie Set-Cookie 标头。

    使用添加 cookie 后 HttpResponse.Cookies 集合,cookie 可立即在 HttpRequest.Cookies 集合,即使尚未发送到客户端响应。

    机翻的中文看起来比较诡异,这里再贴一下原版的英文。

    ASP.NET includes two intrinsic cookie collections. The collection accessed through the Cookies collection of HttpRequest contains cookies transmitted by the client to the server in the Cookie header. The collection accessed through the Cookies collection of HttpResponse contains new cookies created on the server and transmitted to the client in the Set-Cookie header.

    After you add a cookie by using the HttpResponse.Cookies collection, the cookie is immediately available in the HttpRequest.Cookies collection, even if the response has not been sent to the client.

    需要注意的是通过 Response.Cookies.Add() 方法新增的 Cookie 会立即自动新增到 Request.Cookies ,并且这两个 Cookie 的引用地址是相同的。

  4. 通过 Response.Cookies.Set() 方法更新 cookie 的值

    HttpCookieCollection.Set(HttpCookie) Method 中的注解:

    Set 方法首先检查 cookie 是否已存在于集合中,如果是这样对其进行更新。 Set 方法不允许重复的 cookie 的 cookie 集合中。若要在 cookie 集合中添加重复的 cookie,请使用 Add 方法。

    The Set method first checks to see if a cookie already exists in the collection and if so updates it. The Set method does not allow duplicate cookies in the cookie collection. To add duplicate cookies in the cookie collection, use the Add method.

    Set 方法不允许出现重复的 cookie(key 相同的 cookie)。根据上面的说法,如果已存在于集合中,则会更新该 cookie,但我测试时貌似并没有更新(待确认)。

    如果 Response.Cookies 中不存在该 cookie(相同 key),则新增到 Response.Cookies 的同时,也会立即自动新增到 Request.Cookies 。此时,如果 Request.Cookies 中并不存在相同的 cookie,则没有任何问题,规律同上一条一样,RequestResponse 中新增的 cookie 是相同的(地址相同);如果 Request.Cookies 中之前已经存在了一条相同的 cookie,则 Request.Cookies 会出现两条重复的 key,一个 cookie 的引用地址是和 Response.Cookies 中新增的 cookie 是相同的,另一个 cookie 会恢复为最初的值(这个最初的值应该是 HTTP 请求中的 cookie 值)(即使该 cookie 被修改过,甚至已经被从 Request.Cookies 删除了)。

    示例代码如下:

    csharp
    // 获取 Web.config 中的 Session 设置
    System.Web.Configuration.SessionStateSection _sessionSetting =
        ((System.Web.Configuration.SessionStateSection)ConfigurationManager.GetSection("system.web/sessionState"));
    
    var requestSessionCookie = context.Request.Cookies.Get(_sessionSetting.CookieName);
    
    requestSessionCookie.Value = newSessionId; // 新的 session id
    requestSessionCookie.Expires = DateTime.Now.Add(_sessionSetting.Timeout); // 新的过期时间
    // 此时 Request.Cookies 中的值是会同步修改的,因为 requestSessionCookie 就是从 Request.Cookies 中获取的,指向同一个变量
    
    context.Response.Cookies.Set(requestSessionCookie);
    // 此时 Request.Cookies 中会出现重复的 key,一个和 Response.Cookies 中的值相同,另一个会恢复为最初的值(这个值应该是 HTTP 请求中的 cookie 值)
    // 这里如果改成使用 Add 方法,则 Request.Cookies 中仍然会出现两个重复的 key,但是这两个 cookie 的引用地址都是相同的

    这个机制简直匪夷所思!不知道是基于什么理由设计成这样的。暂时也没有找到相关的说明。

  5. Response.Cookies.Get(string name)

    通过 HttpContext.Current.Response.Cookies.Get(string name) 方法获取 cookie 时,若 Response 中不存在会自动创建一个 key 为 name 的 cookie,其值为 "" ,并且会自动同步到 HttpContext.Current.Request.Cookies 中。

    这个特性让我调试时绕了不少弯路,从没想到 Get 方法也会修改 Response.Cookies 的值。
    另外,Request.Cookies.Get() 方法就没有这个问题。

  6. Request.Cookies["sessionid"]

    存在多个 key 为 sessionid 的 cookie 时,通过 HttpContext.Current.Request.Cookies["sessionid"] 或者 HttpContext.Current.Request.Cookies.Get("sessionid") 获取到的是第一个 key 为 sessionid 的 cookie(按照下标从小到大)。