If you are the sort of developer that doesn't tend to use the profiles and membership facilities (for whatever reason...) provided by the .NET framework, then odds are that you will be making your registration process and logging-in facilities. Part of this is the ability to drop a cookie and remember the users when they come and go from your site. There are many approaches to this, some better than others. You could drop an encrypted cookie on the user's computer with the user's ID in it and, upon re-entering the site, you simply log them in and allow them to exist in the system, as if they hadn't left (as is done with Gmail). Another approach is to store their username and the like in a cookie and let the user think that you have remembered them. Then, when the user attempts to take an action that really requires them to identify themselves, you can prompt them to log in again (as LinkedIn appears to do).
CookieWrapper
class in the Models
folder. This class will handle getting, setting, and destroying cookies for us. More than that, we will expose properties, of this class that will give us the data in a strongly typed format, so that we don't have to worry about converting strings all over our application.Models/CookieWrapper.cs:
public class CookieWrapper { private void SetCookie(string key, string value, bool thisSessionOnly) { HttpCookie cookie = new HttpCookie(key, value); if(!thisSessionOnly) cookie.Expires = DateTime.Now.AddMonths(1); HttpContext.Current.Response.Cookies.Add(cookie); } private string GetCookie(string key) { if (HttpContext.Current.Request.Cookies[key] != null) return HttpContext.Current.Request.Cookies[key].Value; return String.Empty; } private void RemoveCookie(string key) { HttpContext.Current.Response.Cookies.Remove(key); } }
AccountID
that will return a Guid
and we will have a Username
property that will return the user's username.Models/CookieWrapper.cs:
public class CookieWrapper { private const string c_accountId = "AccountId"; private const string c_username = "Username"; public Guid AccountID { get { string value = GetCookie(c_accountId); if (!String.IsNullOrEmpty(value)) return new Guid(value); return Guid.Empty; } set { if (value == Guid.Empty) RemoveCookie(c_accountId); else SetCookie(c_accountId, value.ToString(), false); } } public string Username { get { return GetCookie(c_username); } set { if (String.IsNullOrEmpty(value)) RemoveCookie(c_username); else SetCookie(c_username, value, false); } } ... }
Home/Index
view for our display purposes. But we will also create a new action called CookieAction
in HomeController.cs
, which will handle a handful of operations for setting and getting our data out of cookies. These operations will be triggered by some links that we will add to the home page of our application.Controllers/HomeController.cs:
public ActionResult CookieAction(string id) { var cookieJar = new CookieWrapper(); switch(id) { case "SetAccountId": cookieJar.AccountID = Guid.NewGuid(); break; case "SetUsername": cookieJar.Username = "asiemer"; break; case "RemoveAccountId": cookieJar.AccountID = Guid.Empty; break; case "RemoveUsername": cookieJar.Username = String.Empty; break; } return RedirectToAction("Index"); }
Username
and AccountID
into the ViewData
for our home page in the Index
action of the home controller.Controllers/HomeController.cs:
public ActionResult Index() {
ViewData["Message"] = "Welcome to ASP.NET MVC!";
var cookieJar = new CookieWrapper();
ViewData["AccountId"] = cookieJar.AccountID;
ViewData["Username"] = cookieJar.Username;
return View();
}
ViewsHomeIndex.aspx:
Views/Home/Index.aspx:
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2><%: ViewData["Message"] %></h2> <p> To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website"> http://asp.net/mvc</a>. </p> <p> <%: Html.ActionLink("Set AccountId cookie", "CookieAction", new { @id="SetAccountId"}) %> <br /> <%: Html.ActionLink("Remove AccountId cookie", "CookieAction", new { @id="RemoveAccountId"}) %> </p> <p> <%: Html.ActionLink("Set Username cookie", "CookieAction", new { @id = "SetUsername" })%> <br /> <%: Html.ActionLink("Remove Username cookie", "CookieAction", new { @id = "RemoveUsername" })%> </p> <fieldset> <legend>Cookie Jar</legend> Username: <%: ViewData["Username"] %> <br /> AccountId: <%: ViewData["AccountId"] %> </fieldset> </asp:Content>
CookieWrapper
, these cookies will by default be set for a month. Each time you write the cookie, this date will be updated. When closing the browser window and then opening it again, you should see that the state of your cookies have been "remembered".As with our session wrapper in the previous recipe, we have opted to wrap the framework's concept of a cookie, which is exposed by HttpContext
. This gives us a couple of benefits. First and foremost, all of our dealings with the concept of a cookie happen in one placeāhelping to keep our code DRY. Also, it limits the exposure of magic strings that working with cookies often presents in the cookie's dependency on strings for keys. This makes the code that uses our cookie wrapper less fragile and more easily refactorable. All of these things then lead up to one more key strength in wrapping dependencies like this, in that we can easily swap out this implementation for something else for testing purposes or otherwise using an Inversion of Control container, such as StructureMap
.