On The Care and Handling of Cookies

本文详细介绍了ASP.NET中Cookie的工作原理及其使用技巧,包括如何设置过期日期避免常见错误、如何处理无效Cookie以及最佳实践等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Introduction

What exactly is a cookie anyway? According to Websters Online, a cookie is any one of the following:

  1. a small flat or slightly raised cake
  2. a : an attractive woman <a buxom French cookie who haunts the... colony's one night spot -- Newsweek> b : PERSON, GUY <a tough cookie>
  3. cookie : a small file or part of a file stored on a World Wide Web user's computer, created and subsequently read by a Web site server, and containing personal information (as a user identification code, customized preferences, or a record of pages visited)

As tempting as the other definitions may be, what we're looking at here is the third. A cookie is a small packet of information sent as part of the HTTP Response, stored on the client machine and subsequently sent as part of any HTTP Request to the originating web site. In ASP.NET terms, a page receives a CookieCollection as a property of an HttpRequest object (usually Request.Cookies) and returns a CookieCollection of updates as a property of an HttpResponse object (usually Response.Cookies).

Cookies are generally used for various levels of state management: maybe for keeping a user logged on to a site or for keeping track of the last time a site (or area of a site) was last visited.

I recently used cookies to keep track of a tournament signup process, where a team captain might sign up as many as 10 players to a team, one at a time. I was pretty sure that at some point a user's browser might fall over, or either the client or server machine might crash, or a user might click on another link in the menu. I didn't want them to have to start over again, so I stored a Session ID in a cookie and on each signup record in the database. This Session ID is easily retrieved the next time the user comes back to the signup page and I could pick up all the data from the database and save the user a lot of time.

Cookies are a very powerful tool in web design but in ASP.NET they can also be the cause of many problems, especially for users of ASP (which processes cookies slightly differently). Nothing here is rocket science but it is only simple once you understand what's going on behind the scenes.

Cookie Expiration

The first thing you need to understand about cookies is this: Cookies carry an expiry date. The second thing you need to understand is this: Expiry dates are the cause of most cookie-related bugs.

Every time you set the Value of a cookie, remember also to set the Expires date. If you fail to do this you will quickly find yourself losing Cookies owing to them having expired immediately when updating them on the client machine or when the browser closes.

When a cookie expires, the client no longer sends it to the server, so you need to make sure that the Expires property of the cookie is always in the future. If you just set a cookie's value then it will create a cookie with Expires set to DateTime.MinValue (01-Jan-0001 00:00).

You can set a cookie's Expires property using any DateTime value (a positive relief after ASP). For example, if you want a Cookie to expire after the user has not been to that part of your site for a week, you would set Expires = DateTime.Now.AddDays(7).

If you want the cookie to be permanent then the temptation is to use DateTime.MaxValue, as I did in the lat version of this article. However, there is a simple gotcha here.

DateTime.MaxValue is precisely 31-Dec-9999 25:59:59.9999999. But Netscape, even at version 7, doesn't cope with that value and expires the cookie immediately. Amazingly, and somewhat annoyingly, investigation showed that Netscape 7 will cope with 10-Nov-9999 21:47:44 but will not handle a second higher (I'll be honest, I didn't test it to any fraction of a second, I really wasn't interested).

Thus if, like me, you subscribe to the "it doesn't have to look pretty on Netscape, as long as it's functional on the latest version" school of thought, always use a date prior to that. A commonly accepted "permanent" cookie expiry date is DateTime.Now.AddYears(30), ie. 30 years into the future. If someone hasn't visited your site for that long, do you really care what the state was last time they were there?

Disposing of Stale Cookies

If you want to delete the cookie on the client machine, do not use the obvious Response.Cookies.Remove("MyCookie") which simply tells the cookie not to overwrite the client's cookie (see below for a more detailed explanation), set the cookie's Expires property to any time prior to the current time. This will tell the client to overwrite the current cookie with an expired cookie and the client will never send it back to the server.

Again, the temptation is to use DateTime.MinValue (01-Jan-0001 00:00:00) to delete a cookie; again, this would be a mistake. This time, Netscape 7 will work as expected but Internet Explorer 6 considers this to be an exceptional case. If Internet Explorer receives a cookie with what it considers to be a "blank" expiry date, it will retain the cookie until the browser is closed and then expire it.

This could, of course, be useful if the behaviour was consistent across browsers. Unfortunately that is not the case and trying to use the Internet Explorer functionality will cause the page to fail when viewed in Netscape.

Another easy trap to fall into: in theory, you should be able to use DateTime.Now to immediately expire a cookie but there are some dangers in that.

If the server machine time is not quite syncronised with the client machine time, it's possible that the client will think the time given is somewhere in the (admittedly near) future. This can cause a bug to show up when uploaded to a live server that wasn't obvious when testing locally. Worse, it could create a situation where a web application works fine when you view it but not when another user accesses from his machine.

Both situations are notoriously hard to debug.

The safest (and most symmetric) way to delete the cookie by using an Expiry date of DateTime.Now.AddYears(-30).

Incoming Cookies

When a page is received, it has a CookieCollection inside the Request, listing all the cookies in this namespace on the client machine. Any cookies that do not exist in the Request will be null if you try to access them (so be careful of looking for the Value property unless you are sure it exists).

For the Response, on the other hand, there are no cookies when your code begins, they are created as and when you need them. When the server sends back the Response, the client machine only adjusts those Cookies that exist in the Response.Cookies collection; any others are left alone.

In what seems like a bizarre twist of fate, the incoming (Request) cookie carries an Expires date of DateTime.MinValue, regardless of the date attached to the cookie on the client system.

This is actually quite easily explained - as many web developers know, it's near impossible to get hold of the expiry date of a cookie once it is written to the client machine (try it in JavaScript). It certainly isn't sent as part of the request. But Microsoft will have wanted Response and Request cookies to be of the same class (HttpCookie). As DateTime is a value object, rather than a reference object, it cannot be null so it must have a value. The best arbitrary value would be DateTime.MinValue.

Understandable as it is, this is another place we can get caught out if we are not careful. If we want to copy a Request cookie directly to the Response (something we will later see is a useful tool) then we need to create a new expiry date, even if we can safely assume the old date will be okay.

The Mysterious Case of the Disappearing Cookie

If you try to access a cookie that doesn't exist in the Response.Cookies collection, it will be created with an empty string in the Value and an Expires date of 01-Jan-0001 00:00. Strangely, it also creates a matching cookie in the Request.Cookies collection if one doesn't already exist.

So if you look at a cookie in the Response then you are indirectly overwriting the cookie on the client machine with an empty cookie, due to expire when the browser closes (or expiring immediately in Netscape).

A demonstration will help illustrate the point.

Consider a single web page consisting of a label that displays a cookie. Three command buttons each redirect the page to itself, the first sets a cookie, the second clears it and the third does nothing (see image).

For clarity, there is also a groove-style border around the label and the label is, by default, filled it with dashes ("-") so we can see exactly what is happening. ie. we don't want to confuse a blank string with simply not having populated the label.

< asp:label id = " myRequestCookie "
  style
= " Z-INDEX: 101; LEFT: 26px; POSITION: absolute; TOP: 22px "
  runat
= " server "  Width = " 220px "  BorderStyle = " Groove " >
  
-----------------------------------</ asp:label >
< asp:button id = " btnCookies.Set "
  style
= " Z-INDEX: 102; LEFT: 26px; POSITION: absolute; TOP: 56px "
  runat
= " server "  Width = " 220px "  Text = " Set Cookie " ></ asp:button >
< asp:button id = " btnClearCookie "
  style
= " Z-INDEX: 103; LEFT: 26px; POSITION: absolute; TOP: 84px "
  runat
= " server "  Width = " 220px "  Text = " Clear Cookie " ></ asp:button >
< asp:Button id = " btnDoNothing "
  style
= " Z-INDEX: 104; LEFT: 26px; POSITION: absolute; TOP: 112px "
  runat
= " server "  Width = " 220px "  Text = " Do Nothing " ></ asp:Button >
 Collapse
private   void  Page_Load( object  sender, System.EventArgs e)
{
    
// Display the Request cookie on the page
    if (Request.Cookies["TestCookie"== null)
        myRequestCookie.Text 
= "No cookie found";
    
else
        myRequestCookie.Text 
= Request.Cookies["TestCookie"].Value;
}


private   void  btnCookies.Set_Click( object  sender, System.EventArgs e)
{
    
// Set up a cookie and redirect to this page to pick it up for display
    Response.Cookies["TestCookie"].Value = "Cookie is set";
    Response.Cookies[
"TestCookie"].Expires = DateTime.Now.AddYears(30);
    Response.Redirect(
"Example5.1.aspx");
}


private   void  btnClearCookie_Click( object  sender, System.EventArgs e)
{
    
// Expire the cookie and redirect to this page to display a message
    Response.Cookies["TestCookie"].Expires = DateTime.Now.AddYears(-30);
    Response.Redirect(
"Example5.1.aspx");
}


private   void  btnDoNothing_Click( object  sender, System.EventArgs e)
{
    
// Do absolutely nothing except redirect to simulate moving to another page
    Response.Redirect("Example5.1.aspx");
}

 

What you would expect this page to do is always display the latest cookie status: set or null. When the DoNothing button is pressed you would expect the status to remain the same.

Well, guess what. That is exactly what it does do. However...

If you now place a breakpoint on the first line of the Page_Load event handler and add a debugger watch for Response.Cookies["TestCookie"].Value then strange things start happening.

When you set the cookie, it appears to be set, when you clear it or do nothing (regardless of current state), you get an empty string (see image).

This is because the debugger is creating an empty cookie just by looking to see if one is there; this new blank cookie will hang around for as long as the browser is open and then expire. This blank cookie is what now appears in your label.

When you hit the "Set Cookie" button, the first blank response cookie is overriden by a valid (and non-expired) one, so when it returns there is a request cookie which is not automatically overwritten when you break. But when the Response comes back with the label populated correctly, it also has a rogue blank-cookie which immediately expires, so even then the page hasn't worked correctly even though it appears to at first.

This can be extremely dangerous if you are debugging one page which sets the Cookie and then leave the watch visible while debugging another page which doesn't.

Real-world problems

Now that we know exactly what is happening, we can predict potential problems and easily fix them.

The most common predicament is likely to be the case where you want to update a cookie conditionally in one part of the code and then get the "current" value later. Consider the following code carefully:

 
private   void  Page_Load( object  sender, System.EventArgs e)
{
    
// ...

    
// Set cookie only under given situation
    if (myCondition)
    
{
        Response.Cookies[
"MyCookie"].Value = myValue;
        Response.Cookies[
"MyCookie"].Expires = DateTime.Now.AddYears(30);
    }


    
// ...
}


private   void  MyButton_OnClick( object  sender, System.EventArgs e)
{
    
// ...

    
// If there's an updated cookie then get it, otherwise get the old one
    if (Response.Cookies["MyCookie"== null)
        currentCookieValue 
= Request.Cookies["MyCookie"].Value;
    
else
        currentCookieValue 
= Response.Cookies["MyCookie"].Value;

    
// ...
}

As you can guess, because you've just seen the potential problems brutally demonstrated, this code isn't going to work as the developer clearly wants it to. The very act of checking the cookie for null is going to create the cookie. The second condition can never be true and currentCookieValue will be given an empty string wherever the cookie hasn't been explicitly created as a result of the first condition.

This can be inherently difficult to debug because the two pieces of code will probably not be this close together. We've already seen what can happen if you put a Watch on a response cookie so that approach is best avoided and to confuse the developer completely the cookie will expire immediately so it will not be present in the next request.

Prevention is Better Than Cure

The possible solutions here are legion, the most obvious would be to change the second condition to Remove the rogue cookie where it is created.

private void MyButton_OnClick(object sender, System.EventArgs e)
{
    // ...

    // If there's an updated cookie then get it, otherwise get the old one
    if (Response.Cookies["MyCookie"].Value == ""
        && Response.Cookies["MyCookie"].Expires == DateTime.MinValue)
    {
        currentCookieValue = Request.Cookies["MyCookie"].Value;
        Response.Cookies.Remove("MyCookie");
    }
    else
    {
        currentCookieValue = Response.Cookies["MyCookie"].Value;
    }

    // ...
}

Of course, if you have to duplicate this code many times, it is not going to be long before this solution gets unwieldy

A much cleaner solution to the same problem is to make sure that every page likely to update a cookie starts with a copy from the Request.Cookies collection to the Response.Cookies collection; all processing is then done with respect to the response cookie. So the above example becomes:

The one downside of this is that it does create excessive bandwidth usage; you might be sending back a cookie containing the same detail that the client sent. If it is a single small cookie then this is not a serious problem. The odds are high that anything you send back in the cookie collection will be insignificant when compared to the page itself.

 

 Collapse
private   void  Page_Load( object  sender, System.EventArgs e)
{
    
// Ensure preservation of cookie
    if (Request.Cookies["MyCookie"!= null)
        Response.Cookies.Set(Request.Cookies[
"MyCookie"]);
    
else
        Response.Cookies.Set(
new HttpCookie("MyCookie""DefaultValue"));

    
// The Request Cookie doesn't include expiry date, so you need to add one
    
// in either case
    Response.Cookies["MyCookie"].Expires = DateTime.Now.AddYears(30);

    
// ...

    
// Change cookie value only under given situation
    if (myCondition)
        Response.Cookies[
"MyCookie"].Value = myValue;

    
// ...
}


private   void  MyButton_OnClick( object  sender, System.EventArgs e)
{
    
// ...

    
// Response.Cookies will always hold the current value
    currentCookieValue = Response.Cookies["MyCookie"].Value;

    
// ...
}

 

If you do think that bandwidth will be a problem, or if you just want to be super-efficient, the best thing to do is to remove cookies that will not update the original before sending the Response.

 
protected   override   void  OnPreRender(System.EventArgs e)
{
    
// Remember that if the request cookie was null, it
    
// is created by looking at the response cookie
    if (Response.Cookies["TestCookie"].Value == Request.Cookies["TestCookie"].Value)
        Response.Cookies.Remove(
"TestCookie");

    
base.OnPreRender(e);
}

Try applying this technique to the "missing cookie" example, remembering that you should never rely on a null value but use a default (maybe empty) string; you will find you can now debug safely. This is the dowloadable example available at the top of this article.

Always remember to remove the watch when you have finished debugging that page. If the watch is still active when editing a page that may not even look at that specific cookie, you may end up blanking the cookie.

About Paul Riley


Paul lives in the heart of En a backwater village in the middle of England. Since writing his first Hello World on an Oric 1 in 1980, Paul has become a programming addict, got married and lost most of his hair (these events may or may not be related in any number of ways).

Paul's ambition in life is to be the scary old guy whose house kids dare not approach except at halloween.

Click here to view Paul Riley's online profile.

### 类用于机器学习中的模型训练和验证 在机器学习实践中,通常会设计专门的类来处理模型训练与验证的任务。这类结构不仅有助于保持代码整洁有序,还能提高实验过程的可重复性和效率。 #### 训练集特征选择 为了提升模型性能,在构建模型之前可以先筛选出最具信息量的前N个特征[^1]。这一步骤通过计算各个特征与响应标签之间的关联度来进行。具体实现上,可以通过编写`select_features()`方法完成: ```python def select_features(self, X_train, y_train, n=10): """ Select top N most correlated features. Parameters: X_train : array-like of shape (n_samples, n_features) Training data. y_train : array-like of shape (n_samples,) Target values. n : int, default=10 Number of top features to keep. Returns: selected_feature_indices : list[int] Indices of the chosen features. """ correlations = [] for i in range(X_train.shape[1]): corr = np.corrcoef(X_train[:, i], y_train)[0][1] correlations.append(abs(corr)) sorted_correlations_idx = np.argsort(correlations)[-n:] return sorted_correlations_idx.tolist() ``` 此函数接收原始数据矩阵以及目标向量作为输入参数,并返回所选特征对应的索引列表。 #### 模型评估指标 当完成了模型训练之后,则需依赖一系列评价标准去衡量其表现优劣程度。常见的评估方式涵盖了混淆矩阵、精确率(Precision)、召回率(Recall)等在内的多种统计学测度[^2]。下面是一个简单的例子展示如何定义此类功能: ```python from sklearn.metrics import confusion_matrix, precision_score, recall_score class ModelEvaluator(object): def __init__(self, true_labels, pred_labels): self.true_labels = true_labels self.pred_labels = pred_labels def get_confusion_matrix(self): """Return Confusion Matrix.""" cm = confusion_matrix(self.true_labels, self.pred_labels) return cm def calculate_precision(self): """Calculate Precision Score""" prec = precision_score(self.true_labels, self.pred_labels) return prec def calculate_recall(self): """Calculate Recall Score""" rec = recall_score(self.true_labels, self.pred_labels) return rec ``` 上述代码片段展示了创建一个名为`ModelEvaluator`的对象实例化时传入真实标记值(`true_labels`)及预测结果(`pred_labels`);随后提供了获取混淆矩阵、精度得分和召回率的方法接口供外部调用查询。 #### 应用案例分析 假设正在开发一套垃圾邮件分类器应用正则化的逻辑回归算法进行建模工作。经过一段时间的学习调整后得到如下所示的结果表格[^3]: | 实际类别 | 预测为1(垃圾) | 预测为0(正常) | | --- | --- | --- | | 正确识别为1 | 85 | 15 | | 错误判断为0 | 890 | 10 | 从表中可以看出该模型对于非垃圾邮件有着较高的准确率但是却经常错误地标记正常的邮件成为垃圾邮件。针对这种情况可能需要重新审视当前使用的特征集合或是尝试其他类型的分类算法以改善整体效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值