Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Application level Cookie Management for ASP.NET apps


:P
On this page:

When I build Web applications I usually have at least one persistent cookie for that application that needs to be accessed from a number of locations inside of the application. While Cookie management isn’t exactly rocket science I found myself writing the same code over and over again so when I built my ASP.NET framework I added some basic Cookie handling into each application.

 

Basically I create a custom class for the permanent and often used Cookie that handles all the mechanics of retrieving the cookies, checking for it, creating new Id if needed and getting rid of the permanent cookie. ASP. Net makes all this pretty easy, but if you do this directly in your ASP. Net application code you’ll end up with a lot of hard to maintain code smattered all over the place. If your cookie logic changes you end up having to hunt it down. The app specific classes address this.

 

If you’ve used the West Wind Web Store there is a class called WebStoreCookie that manages all of this. The original class was a static class, so from anywhere in the application you could simply do:

 

string UserId = WebStoreCookie.GetUserId();

 

and it would always return a user id that was either retrieved from a cookie, or just generated and then automatically stored to the Cookie object.

 

Today I looked at that code and figured that I’m using it in just about every application I build. So far I’d just been cutting & pasting it and customizing the Cookie Id. So it was time to make this a generic class that can be easily reused.

 

I liked the idea of a static class because you can simply reference the cookie. The static version looks something like this:

 

/// <summary>

/// A generic Web Cookie handler class that can be used for a single 'UserId' in an

/// application. This class manages all aspects of retrieving and setting of a cookie

/// consistently. Typically all you'll need to do is call the GetId() method which

/// both returns existing cookies and ensures that the cookie gets set.

///

/// All methods of this class are static which is the reason why only a single

/// </summary>

public class wwStaticCookie

{

      public static string CookieName = "WestWindUser";

      public static int CookieTimeoutInMonths = 24;

 

      /// <summary>

      /// Writes the cookie into the response stream with the value passed. The value

      ///  is always the UserId.

      /// <seealso>Class WebStoreCookie</seealso>

      /// </summary>

      /// <param name="String Value">

      /// Writes a value into the specified cookie.

      /// </param>

      /// <returns>Void</returns>

      public static void WriteCookie(string Value, bool NonPersistent)

      {

            HttpCookie Cookie = new HttpCookie(CookieName,Value);

           

            if (!NonPersistent)

                  Cookie.Expires = DateTime.Now.AddMonths(CookieTimeoutInMonths);

 

            HttpContext.Current.Response.Cookies.Add(Cookie);

      }

 

      /// <summary>

      /// Writes the cookie into the response stream with the value passed. The value

      ///  is always the UserId.

      /// <seealso>Class WebStoreCookie</seealso>

      /// </summary>

      /// <param name="String Value">

      /// Writes a value into the specified cookie.

      /// </param>

      /// <returns>Void</returns>

      public static void WriteCookie(string Value)

      {

            WriteCookie(Value,false);

      }

 

     

      /// <summary>

      /// Removes the cookie by clearing it out and expiring it immediately.

      /// <seealso>Class WebStoreCookie</seealso>

      /// </summary>

      /// <returns>Void</returns>

      public static void Remove()

      {

            HttpCookie Cookie =  HttpContext.Current.Request.Cookies[CookieName];

            if (Cookie != null)

            {

                  Cookie.Expires = DateTime.Now.AddHours(-2);

                  HttpContext.Current.Response.Cookies.Add( Cookie );

            }

      }

 

      /// <summary>

      /// Retrieves the user's Cookie. If the Cookie doesn't exist a new one is generated

      /// by hashing a new GUID value and writing the Cookie into the Response.

      /// <returns>Customers UserId</returns>

      public static string GetId()

      {

            string UserId;

 

            // *** Check to see if we have a cookie we can use

            HttpCookie Cookie =  HttpContext.Current.Request.Cookies[CookieName];

            if (Cookie == null)

                  UserId = null;

            else

                  UserId = (string) Cookie.Value;

 

            if (UserId == null)

            {

                  // *** Generate a new ID

                  UserId = Guid.NewGuid().ToString().GetHashCode().ToString("x");

                  WriteCookie(UserId);

            }

            return UserId;

      }

 

      /// <summary>

      /// Determines whether the cookie exists

      /// </summary>

      /// <returns></returns>

      public static bool CookieExist()

      {

            // *** Check to see if we have a cookie we can use

            HttpCookie loCookie =  HttpContext.Current.Request.Cookies[CookieName];

            if (loCookie == null)

                  return false;

           

            return true;

      }

     

}

 

This actually works well IF you only need a single instance of a cookie for an application. The reality is that in most applications there is only a single instance of an ID cookie, so this may actually be fully sufficient. A single cookie? You bet - all other settings are stored in sessions or more permanent user profiles and the single cookie merely serves as a master Id that the user can be remembered by.

 

Why only a single instance of the class though, you ask? Because the properties and methods are static, there’s no easy way to assign a unique name to a cookie (the CookieName property). So if you were to subclass wwStaticCookie into WebStoreCookie and AdminCookie you’d end up with two classes that both would use the same CookieName which is not what you'd want. You also can’t override the properties easily with a static constructor because the subclass likely won’t have any properties of its own or none that would reliably fire the constructor to update the CookieName. So in addition to the single instance issue there’s also the issue of how do you set the cookiename. You pretty much have to do this somewhere in the startup code of your Web application. I usually do this in a static constructor of an Application object, but it could go anywhere in the application’s startup.

 

All of this is workable for a single cookie, but even then not great <g>…

 

So after some back and forth I decided you really need an instance class to do this right. The instance version is nearly the same as the static one – it just removes the static properties and methods. It’d be really cool if you could create polymorphic methods for static and non-static with the same signatures but you can’t, so there are two separate classes.

 

Here’s the non-static version:

 

/// <summary>

/// A generic Cookie class that manages an individual Cookie by localizing the

/// cookie management into a single class. This means the Cookie's name and

/// and timing is abstracted.

///

/// The GetId() method is the key method here which retrieves a Cookie Id.

/// If the cookie exists it returns the value, otherwise it generates a new

/// Id and creates the cookie with the specs of the class and

///

/// It's recommended you store this class as a static member off another

/// object to have

/// </summary>

public class wwCookie

{

      /// <summary>

      /// The name of the Cookie that is used. This value should always be set or

      /// overridden via the constructor.

      /// <seealso>Class wwCookie</seealso>

      /// </summary>

      public string CookieName = "WestWindUser";

 

      /// <summary>

      /// The timeout of a persistent cookie.

      /// <seealso>Class wwCookie</seealso>

      /// </summary>

      public int CookieTimeoutInMonths = 48;

 

      public wwCookie()

      {

      }

 

      public wwCookie(string NewCookieName)

      {

            this.CookieName = NewCookieName;

      }

 

      /// <summary>

      /// Writes the cookie into the response stream with the value passed. The value

      /// is always the UserId.

      /// <seealso>Class WebStoreCookie</seealso>

      /// </summary>

      /// <param name="Value"></param>

      /// <param name="NonPersistent"></param>

      /// <returns>Void</returns>

      public void WriteCookie(string Value, bool NonPersistent)

      {

            HttpCookie Cookie = new HttpCookie(CookieName,Value);

           

            if (!NonPersistent)

                  Cookie.Expires = DateTime.Now.AddMonths(CookieTimeoutInMonths);

 

            HttpContext.Current.Response.Cookies.Add(Cookie);

      }

 

      /// <summary>

      /// Writes the cookie into the response stream with the value passed. The value

      /// is always the UserId.

      /// <seealso>Class WebStoreCookie</seealso>

      /// </summary>

      /// <param name="Value"></param>

      /// <returns>Void</returns>

      public void WriteCookie(string Value)

      {

            WriteCookie(Value,false);

      }

 

     

      /// <summary>

      /// Removes the cookie by clearing it out and expiring it immediately.

      /// <seealso>Class WebStoreCookie</seealso>

      /// </summary>

      /// <param name=""></param>

      /// <returns>Void</returns>

      public void Remove()

      {

            HttpCookie Cookie =  HttpContext.Current.Request.Cookies[CookieName];

            if (Cookie != null)

            {

                  Cookie.Expires = DateTime.Now.AddHours(-2);

                  HttpContext.Current.Response.Cookies.Add( Cookie );

            }

      }

 

      /// <summary>

      /// This is the key method of this class that retrieves the value of the

      /// cookie. This method is meant as retrieving an ID value. If the value

      /// doesn't exist it is created and the cookie set and the value returned. If

      /// the Cookie exists the value is retrieved and returned.

      /// <seealso>Class wwCookie</seealso>

      /// </summary>

      /// <param name=""></param>

      /// <returns>String</returns>

      public string GetId()

      {

            string UserId;

 

            // *** Check to see if we have a cookie we can use

            HttpCookie Cookie =  HttpContext.Current.Request.Cookies[CookieName];

            if (Cookie == null)

                  UserId = null;

            else

                  UserId = (string) Cookie.Value;

 

            if (UserId == null)

            {

                  // *** Generate a new ID

                  UserId = this.GenerateId();

                  WriteCookie(UserId);

            }

            return UserId;

      }

 

      /// <summary>

      /// Method that generates the ID stored in the cookie. You can override

      /// this method in a subclass to handle custom or specific Id creation.

      /// </summary>

      /// <returns></returns>

      protected virtual string GenerateId()

      {

            return Guid.NewGuid().ToString().GetHashCode().ToString("x");

      }

 

      /// <summary>

      /// Determines whether the cookie exists

      /// <seealso>Class wwCookie</seealso>

      /// </summary>

      /// <param name=""></param>

      /// <returns>Boolean</returns>

      public bool CookieExist()

      {

            // *** Check to see if we have a cookie we can use

            HttpCookie Cookie =  HttpContext.Current.Request.Cookies[CookieName];

            if (Cookie == null)

                  return false;

           

            return true;

      }

}

 

In order to use this class you’d subclass it to override the CookieName and Cookie Expiration property and to give it an application specific name:

 

public class WebStoreCookie : wwCookie

{

      public WebStoreCookie(string CustomCookieName) : base(CustomCookieName) { ; }

 

      /// <summary>

      /// Retrieves the Customer's user from a Cookie. If the cookie doesn't

      /// exist the cookie is created and written to the Response.

      /// </summary>

      /// <param name="loPage">Asp.net Page object to access Request and REsponse objects</param>

      /// <returns>Customers UserId</returns>

      public string GetUserId()

      {

            return this.GetId();   

      }

}

 

In this case I also added another method that matched the previous name of my old class that retrieves a UserId for the application.

 

Next you create a property on one of your application’s classes. I usually have an Application class or a Utilities class of some sort. In this case I use the WebStoreUtilities class because it’s specific to the Web application as opposed to the Application class which deals with application business logic specific settings (ie. it’s used both in the Web and WinForms interface).

 

public class WebStoreUtils

{

      public static WebStoreCookie WebStoreCookie =

             new WebStoreCookie("WebStoreUser");

     

         

}

 

Now you can access this cookie consistently throughout your app with:

 

string ID = WebStoreUtils.WebStoreCookie.GetUserId();

 

 

This is a nice way to expose non-static classes/members to an application without having to create specific instances each time you use them by the way – this pattern applies to a number situations. For example, I use the same approach with my configuration class.

 

Anyway, this class has really made cookie handling a lot easier in my applications. When I made these changes I just found 25 place places where the Cookie was used and in all but one of those places a single line was required to deal with the Cookie, which just proves that it pays to abstract this logic out of the application code base.

 

Now go have some real (chocolate chip) cookies instead of 'virtual' ones...

  


The Voices of Reason


 

OdeToCode Link Blog
October 26, 2004

# New


nour
October 26, 2005

# re: Application level Cookie Management for ASP.NET apps

Do you know your cookie? (When you write a cookie, you need to get it
back so how can you get it back if you don't know the name of the
cookie?)
So my question is that make sure you can write a cookie and pull it
back to make sure you can get it.
how can i do this

I want to know this question too
November 17, 2005

# re: Application level Cookie Management for ASP.NET apps

how can I get all cookies in the same domain?
THANKS-_-

Rick Strahl's WebLog
January 10, 2006

# Loosing a Cookie when switching from http:// to https://

I have yet another odd cookie situation - when using a cookie that is pathed to a virtual directory, switching from http to https causes the cookie to get lost.

Willie Tilton
May 30, 2006

# Managing cookies


cg
September 04, 2006

# re: Application level Cookie Management for ASP.NET apps

This is probably a bit more succinct:

public static bool CookieExist() {
return (HttpContext.Current.Request.Cookies[CookieName] == null);
}

Joe
September 21, 2006

# re: Application level Cookie Management for ASP.NET apps

I just don't get one thing. Where do I put this - it is a generic calss for dealing with cookies. Great. So i want to put it in a class library that a bunch of my sites can call. There is a problem - i can't use HttpCookie in a class library. What am I missing?

# 关于Asp.net中Cookie的问题 - 李洪根的blog-专注软件开发技术 - CSDNBlog

关于Asp.net中Cookie的问题,如中文乱码、Remove清除的问题。

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024