.NET Request.Cookies & Response.Cookies
记录一下最近在调查禁用 .NET 自带的 Session 机制,但是仍然使用 cookie 保存一个类似 SessionId 的值时遇到了以前一直不曾注意的问题。
启用 .NET 默认的 Session 的情况下,并不是第一次请求就会创建 SessionId ,而是在第一次使用
Session保存数据时才会生成一个 SessionId 保存到客户端的 cookie。调用
Response.Cookies.Clear()方法并不会清除客户端的 cookie。这是在现在项目中发现的遗留 bug。正确的方法是获取
Request.Cookies中每个 cookie,设置过期时间为之前的日期之后再新增到Response.Cookies。示例代码:
csharpvar 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); }1
2
3
4
5
6
7
8
9
10
11
12
13通过
Response.Cookies.Add()方法新增 cookie 到客户端可以参考 HttpResponse.Cookies Property 和 HttpRequest.Cookies Property 中的注解。
ASP.NET 包括两个内部 cookie 集合。通过访问的集合
Cookies的集合HttpRequest包含传输中的服务器到客户端的 cookieCookie标头。通过访问的集合Cookies的集合HttpResponse包含在服务器上创建和传输到客户端中的新 cookieSet-Cookie标头。使用添加 cookie 后
HttpResponse.Cookies集合,cookie 可立即在HttpRequest.Cookies集合,即使尚未发送到客户端响应。机翻的中文看起来比较诡异,这里再贴一下原版的英文。
ASP.NET includes two intrinsic cookie collections. The collection accessed through the
Cookiescollection ofHttpRequestcontains cookies transmitted by the client to the server in theCookieheader. The collection accessed through theCookiescollection ofHttpResponsecontains new cookies created on the server and transmitted to the client in theSet-Cookieheader.After you add a cookie by using the
HttpResponse.Cookiescollection, the cookie is immediately available in theHttpRequest.Cookiescollection, even if the response has not been sent to the client.需要注意的是通过
Response.Cookies.Add()方法新增的 Cookie 会立即自动新增到Request.Cookies,并且这两个 Cookie 的引用地址是相同的。通过
Response.Cookies.Set()方法更新 cookie 的值HttpCookieCollection.Set(HttpCookie) Method 中的注解:
Set方法首先检查 cookie 是否已存在于集合中,如果是这样对其进行更新。Set方法不允许重复的 cookie 的 cookie 集合中。若要在 cookie 集合中添加重复的 cookie,请使用Add方法。The
Setmethod first checks to see if a cookie already exists in the collection and if so updates it. TheSetmethod does not allow duplicate cookies in the cookie collection. To add duplicate cookies in the cookie collection, use theAddmethod.Set方法不允许出现重复的 cookie(key 相同的 cookie)。根据上面的说法,如果已存在于集合中,则会更新该 cookie,但我测试时貌似并没有更新(待确认)。如果
Response.Cookies中不存在该 cookie(相同 key),则新增到Response.Cookies的同时,也会立即自动新增到Request.Cookies。此时,如果Request.Cookies中并不存在相同的 cookie,则没有任何问题,规律同上一条一样,Request和Response中新增的 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 的引用地址都是相同的1
2
3
4
5
6
7
8
9
10
11
12
13这个机制简直匪夷所思!不知道是基于什么理由设计成这样的。暂时也没有找到相关的说明。
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()方法就没有这个问题。Request.Cookies["sessionid"]存在多个 key 为 sessionid 的 cookie 时,通过
HttpContext.Current.Request.Cookies["sessionid"]或者HttpContext.Current.Request.Cookies.Get("sessionid")获取到的是第一个 key 为 sessionid 的 cookie(按照下标从小到大)。