Bake ASP.NET Apps With Cookie Classes
Take control of browser cookies with the .NET Framework's HttpCookie and HttpCookieCollection classes.
October 30, 2009
ClassAct
LANGUAGES: VB .NET
TECHNOLOGIES: Cookies
Bake ASP.NET Apps With Cookie Classes
Take control of browser cookies with the .NETFramework's HttpCookie and HttpCookieCollection classes.
By Ken Getz
HTTP cookies allow a Web server to communicate with abrowser making the request for a page. Each cookie takes the form of aname/value pair, passed between the server and the client in the HTTP header.Cookies are stored either in memory (and are only valid for the currentsession) or are persisted to disk if the Web page designer assigns them anexpiration date in the future. In memory, the HTTP header includes a stringlike this one, which contains multiple cookies:
ASP.NET_SessionId=4mqbrkbd35cibwychzbuzw3b;
UserName=Mary; FontInfo=FontName=Tahoma&
FontColor=DeepSkyBlue&FontSize=Medium
In this example, the HTTP header contains three cookies.First, ASP.NET_SessionID containsthe session ID ASP.NET uses to retrieve session variables from its internallookup table. Next, UserNamecontains the value "Mary." Finally, FontInfocontains multiple named values: FontName,FontColor, and FontSize.
This simple example points out some interesting facts.First of all, ASP.NET (and ASP) relies on a cookie to maintain the sessionstate - without that cookie, there's no way for ASP to track the currentsession. (Luckily, ASP.NET offers an alternative to using a cookie for thispurpose as many users disable the availability of cookies from within theirbrowsers. See documentation in the web.config file for more information.)Cookies can contain a single value (as in the case of the UserName cookie) or multiple values (as with the FontInfo cookie). If a cookie containsmultiple values, it appears in the HTTP header much like a standard querystring within an HTTP request, with name/value pairs separated by an ampersand(&).
Cookies cannot contain anything but text because they'repassed in the HTTP header, which limits their capabilities. They also must berelatively small - less than 1,024 bytes. So you can't store objects in cookiesunless you serialize them as small amounts of text. But that's not really whatcookies were meant for. They were meant only to pass small amounts of textualinformation between the client and the server, stored on the client side.
If the Web site you're visiting creates one or morecookies and assigns them a future expiration date, your browser creates acookie file on disk corresponding to the response to your request for data. Forexample, after I visited the sample site you'll investigate throughout thisarticle, I found a cookie file on my hard drive (see Figure 1).
Figure 1. Requesting a page from a site that creates persistent cookiesadds cookie files to your hard drive. The files don't get deleted even afterthe cookies expire, so you might want to clean this folder occasionally.
Corresponding to the HTTP header information shownpreviously, the keng@localhost[2].txt file contained this text:
UserName
Mary
localhost/
1024
351393792
29499798
3124092368
29499742
*
FontInfo
FontName=Tahoma&FontColor=DeepSkyBlue&FontSize=Medium
localhost/
1024
351393792
29499798
3124092368
29499742
*
Although most of the contents are meaningless to humanreaders, it's still relatively clear how this text corresponds to the in-memoryHTTP headers. In any case, cookie files are simply text and available to anyonewho can access the folder containing them. You should be careful about whattypes of information you place into users' cookies; that is, you want to storeinformation your site can use to locate information in server-side databases -you won't want to store confidential information in a user's cookie.
When you request a page from a specific URL, the browsercan look in your cookies folder and find the cookie file corresponding to thesite you're requesting a page from. If it finds the file, the browser will loadthe contents into memory and insert them into each subsequent HTTP request tothe Web server. ASP.NET can then retrieve the name/value pairs from theHTTP_COOKIE section of the HTTP request header and use them to determine thepage's HTML output. Additionally, ASP.NET can tell the browser to add, delete,or update the cookies with new values and expiration dates by including them inthe HTTP response's header.
As you'll see, the .NET Framework makes working withcookies trivially easy. I created sample page (see Figure 2) that demonstratesthe concepts involved in creating and retrieving cookies. It's not terriblyelegant, but it shows off several features involved in creating and consumingcookies. Most of the code you'll find in the sample manages the drop-down listson the page. Very little of the page's code handles the cookies; they don'trequire much effort.
Figure 2. The sample page allows you to enter a name, and select variousfont attributes for the controls on the page. The page saves the name into asingle cookie, and all the font information into a second cookie.
Use the HttpCookie andHttpCookieCollection Classes
Both the Requestand Response properties of a page -that is, HttpRequest and HttpResponse objects provided asproperties of an object and of the CurrentContextproperty of a thread - provide a Cookiesproperty. This property is an HttpCookieCollectionobject that provides the methods you might expect for working with a collectionof objects, such as Add, Clear, Remove, and Set (the Set method updates the value of anexisting cookie). The HttpCookieCollectionclass also provides useful methods and properties for manipulating key/valuepairs, such as the GetKey method, whichreturns the key at a particular index within the collection; and the AllKeys property, which returns astring array containing all the key names within the collection. To retrieve asingle cookie from the Request or Response object's Cookies property (I think this is the technique you'll use mostoften), write code like this:
If NotRequest.Cookies("UserName") Is Nothing Then
txtName.Text =Request.Cookies("UserName").Value
End If
This example determines whether the browser sent the UserName cookie in the current requestand if so, displays its value in a text box on the page. Note that you mustcheck to ensure a cookie isn't Nothingbefore you work with it; attempting to retrieve properties or call methods ofan object that doesn't exist causes a NullReferenceExceptionto be thrown.
The HttpCookieclass provides a number of useful properties, and no methods specific to theclass. You can call the class's constructor explicitly, passing in either aname for the cookie, or a name and a value for the cookie, like this:
Dim ck As New HttpCookie("UserName")
ck.Value = "Mary"
' or
Dim ck As New HttpCookie("UserName", "Mary")
Note the lack of a default constructor in this example. Ifyou use the constructor, you'll need to supply at least a name. You also canretrieve the Name or Value properties once you've createdthe cookie.
To force a cookie to be preserved across sessions, you canset its expiration date to some date and time in the future using the Expires property. For example, thiscode causes the cookie to be available for the next 24 hours:
ck.Expires = Today.AddDays(1)
To demonstrate this behavior, try browsing to Cookies.aspxfrom the sample project. On the sample page, try making some changes to thecontrols, then click on the Home link to browse to anotherpage within the same session. Now, navigate back to the Cookies.aspx page -your cookies remain in memory. But if you close the session and restart, yoursettings disappear. If you repeat the experiment after selecting the Save Information? check box, however, your settingswill be preserved across sessions for up to 24 hours.
Cookies can contain more than one value, and you can addname/value pairs to the Values propertyof a single cookie. For example, the sample page stores all the fontinformation in a single cookie. This code, from Cookies.aspx, retrieves fontinformation from the DropDownListcontrols on the page and stores it all into a single cookie:
Dim ckFont As New HttpCookie("FontInfo")
ckFont.Values("FontName") = ddlFont.SelectedItem.Text
ckFont.Values("FontColor") = ddlColor.SelectedItem.Text
ckFont.Values("FontSize") = ddlSize.SelectedItem.Text
To retrieve one of these "subitems," use syntax like this:
Dim ck As HttpCookie
ck = Request.Cookies("FontInfo")
Dim strTemp As String
strTemp = ck.Values("FontName")
This code retrieves a cookiefrom the page's Request object, thenretrieves one of its subvalues.
Investigate the Project
When you first load the sample page Cookies.aspx in abrowser, the browser can't locate the associated cookie file (obviously,ASP.NET hasn't saved it on the client yet). In this case, your request doesn'tinclude any cookie information, so no cookie values are loaded. Try out thesample page: Navigate to Cookies.aspx in a browser window, enter a name, andselect various font attributes. Click on the Submitbutton to trigger a post-back, updating the page display. After each roundtrip, you should see the changes you requested and the values for cookies fromthe previous round trip. Why are the cookies "out of date"? Remember, you'reseeing the cookie values as they're submitted in the request - as they'restored on the client. These are always one version old, compared to the latestsettings you see on the page.
When you post back to the page, code in the page's Load event handler managing thepost-back creates cookies in the Responseobject's Cookies collection based onthe values you've sent to the page. These cookies are sent back to the browser,which simply posts them back to the server in the next request or, if youchoose, also saves the cookies to disk (you must set the expiration date inyour code to do this). The code in Figure 3 runs in the sample page's Loadevent handler.
If Page.IsPostBack Then
Response.Cookies("UserName").Value = txtName.Text
Dim ckFont As NewHttpCookie("FontInfo")
ckFont.Values("FontName") = ddlFont.SelectedItem.Text
ckFont.Values("FontColor") = ddlColor.SelectedItem.Text
ckFont.Values("FontSize") = ddlSize.SelectedItem.Text
Response.Cookies.Add(ckFont)
If chkSaveCookies.Checked Then
' Expire the cookie inone day.
Response.Cookies("UserName").Expires = _
Today.AddDays(1)
Response.Cookies("FontInfo").Expires = _
Today.AddDays(1)
End If
Else
' Code removed here...
Figure 3. If you're posting back to the sample page,code in the page's Load event handler adds a cookie to the HttpResponseobject's Cookies collection.
Note that the code in Figure 3 uses an alternativetechnique for creating a new cookie. Instead of instantiating a new HttpCookie object, setting its value,and adding to the Cookiescollection, you can allow the collection to create the cookie on the fly usingthis code:
Response.Cookies("UserName").Value= txtName.Text
It's the equivalent to this:
Dim ck As New HttpCookie("UserName", txtName.Text)
Response.Cookies.Add(ck)
If the page isn't posting to itself, the code will attemptto retrieve existing cookies from the Requestobject; if it finds existing cookies, it renders the page according the savedvalues it finds (see Figure 4).
If Page.IsPostback Then
' Code removed here...
Else
If NotRequest.Cookies("UserName") Is Nothing Then
txtName.Text =Request.Cookies("UserName").Value
End If
If Not Request.Cookies("FontInfo") Is Nothing Then
Dim ck As HttpCookie =Request.Cookies("FontInfo")
' Handle fontinformation.
Dim strTemp As String
If ck.HasKeys Then
strTemp =ck.Values("FontName")
SelectItem(ddlFont,strTemp)
SetFontName(strTemp)
' Repeated for font size and color.
End If
End If
End If
Figure 4. If you're not posting back to the samplepage, the Load event handler attempts to load information from the cookiepassed in the HttpRequest object's Cookies collection.
You've seen much of the code from Figure 4 previously.Note that this fragment not only checks to ensure that the FontInfo cookie exists, it also uses the HasKeys property to verify that the cookie has subkeys:
If NotRequest.Cookies("FontInfo") Is Nothing Then
Dim ck As HttpCookie =Request.Cookies("FontInfo")
' Handle fontinformation.
Dim strTemp As String
If ck.HasKeys Then
For each of the three font cookie values, the coderetrieves the value, selects the matching value in the DropDownList control, and calls code to iterate through all thecontrols on the page, setting the appropriate property:
strTemp = ck.Values("FontName")
SelectItem(ddlFont, strTemp)
SetFontName(strTemp)
Dig Deeper
You've now seen all the code you'll find in this article'ssample project (see the Download box for details), but the HttpCookie and HttpCookieCollectionclasses provide more methods and properties than those you've seen here. If youneed to manipulate cookies within the Requestor Response object's Cookies property, take some time tolook into the HttpCookieCollection'sdocumentation. For working with a single cookie, you'll need to investigate theHttpCookie class's documentation.Specifically, this class provides three properties not covered here: Domain, Path, and Secure. Theseproperties determine whether or not the cookie is transmitted to the client. Ifyou set the Domain property, you canlimit the cookie's transmission to clients requesting the cookie from thespecific domain. For example, on my server, if I set the Domain property of a cookie to "microsoft.com", the cookie nevergets sent to the client - the client didn't request the cookie frommicrosoft.com, so the server didn't send it. The Path property lets you build a complete path for a specific URLthat the cookie applies to. ASP.NET combines this property value with the Domain property value to create thefull path associated with the cookie. Finally, the Secure property allows you to specify that the cookie istransmitted only over secure connections (that is, only if the request was madeusing the HTTPS protocol).
A special thanks to Andy Baron for providing the basis forthis demonstration, as part of the ASP.NET courseware we co-wrote with MaryChipman for Application Developer's Training Company (http://www.appdev.com). In addition, thanksto Paul D. Sheriff, who reminded me of the steps required to iterate throughall the controls on a page. This is one of those obscure techniques that'suseful, but not terribly obvious.
The sample code in thisarticle is available for download.
Ken Getz is asenior consultant with MCW Technologies and splits his time betweenprogramming, writing, and training. He specializes in tools and applicationswritten in Visual Studio .NET and Visual Basic, and he is co-author of Access 2002 Developer'sHandbook Desktop Edition as well as the training materials for AppDev'sASP.NET, Access 97 and 2000, Visual Basic 5.0, and Visual Basic 6.0 classes. Hespeaks frequently at technical conferences and is a contributing writer for asp.netPRO. E-mailKen at mailto:[email protected].
TheSelectItem and SetFontName Procedures
Although not directly related to the HttpCookie class, The SelectItem and SetFontNameProcedures are interesting. The SelectItemmethod allows you to specify a DropDownListcontrol and the text of the item you want to select. It uses the FindByText method of the control's Items collection, then passes the valueits ListItem object finds to thecontrol's IndexOf method. Finally,given the index of the item you specify, the code sets the SelectedIndex property of DropDownList:
Private Sub SelectItem( _ ByVal ddlAs DropDownList, ByVal Value As String)
ddl.SelectedIndex = _
ddl.Items.IndexOf(ddl.Items.FindByText(Value))
End Sub
The SetFontNameprocedure (along with the SetFontSizeand SetFontColor procedures)iterates through all the controls on the page, skipping HyperLink controls (hyperlinks look odd if you modify their fontsand colors). It's not obvious how to visit each control on the page until youknow the trick: The HtmlForm objectcorresponding to your page's HTML
tag is the actual container of the controls, and the form isexposed as a control within the page. The SetFontNameprocedure (with its exception handling and special casing for the HyperLink control removed) looks likethis:
Private Sub SetFontName(ByVal FontName As String)
Dim ctl As Control
For Each ctl In Page.FindControl("Form1").Controls
CType(ctl,WebControl).Font.Name = FontName
End Sub
The other two related procedures are almost identical.These procedures use the Pageobject's FindControl method tolocate the HtmlForm containing allthe controls. For this sample page, the HtmlFormobject's name is Form1 (you can view the page in HTML view to verify the name),so that's the control the code locates using the FindControl method. (To be honest, I was tempted to use delegatesto shorten this code - it's awfully repetitive in the sample page. But doing sowould have complicated the page, and I would have used more space explainingthe delegate handling than in writing the HttpCookieclass itself.)
Tell us what you think! Please send any comments about thisarticle to [email protected] include the article title and author.
About the Author
You May Also Like