ASP.NET MVC working smart with cookies – Custom ValueProvider

ValueProviders are the mechanism within the MVC framework that tries to take information from the HTTP Request and then resolve parameter values on action methods. Complex as well as simple types will be resolved using one of the four inbuilt value providers listed.

  • FormValueProvider: Provides values from posted form parameters.
  • QueryStringValueProvider: Provides values from the query string collection.
  • HttpFileCollectionValueProvider: Provides values from posted files.
  • RouteDataValueProvider: Provides values from route parameters.

Below I have created a controller which has one action. When the Index action is called using GET a cookie is added to the browser called isRegistered with the value “true” assigned to it. When the form is posted the index action is again called and the cookie value is automatically assigned to the isRegistered parameter on the POST index method. But how?  By using a custom Value Provider!

    public class PersonController : Controller
                {
                    [HttpGet]
                    public ActionResult Index()
                    {
                        AddCookie();
                        ViewData["CookieData"] = false;
                        return View(new Employee { CreateDate = new DateTime(2010, 10, 10) });
                    }
                    [HttpPost]
                    public ActionResult Index(Employee employee, bool isRegistered)
                    {
                        ViewData["CookieData"] = isRegistered;
if (!ModelState.IsValid) return View(employee); return View(employee); } private void AddCookie() { HttpContext.Response.Cookies.Add( new HttpCookie("isRegistered", "true") ); } }

Below I have the mark-up for a checkbox that looks at the value assigned to the ViewData key “CookieValue”.

            <div>
                            <label>Registered</label>
                            <span><%=Html.CheckBox("Registered", (bool)ViewData["CookieData"])%></span><br />
                        </div>

When the form is requested the registered checkbox is unchecked. After the form is posted to the server the registered checkbox is set to checked as the CookieValueProvider resolves the value isRegistered by looking at all the cookies stored for the site.  (Note: the registered checkbox does not have an ID or NAME so it is not resolved by the FormValueProvider)

image 
Below is an implementation of the CookieValueProviderFactory and also CookieValueProvider. By convention when the CookieValueProvider is invoked by the framework it will try to match the name of variable passed to it from the action method signature to a cookie with the same name. If a match is found the variables value will be set.

    public class CookieValueProviderFactory : ValueProviderFactory
                {
                    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
                    {
                            return new CookieValueProvider(controllerContext.HttpContext.Request.Cookies);
                    }
                    private class CookieValueProvider : IValueProvider
                    {
                        private readonly HttpCookieCollection _cookieCollection;
            
                        public CookieValueProvider(HttpCookieCollection cookieCollection)
                        {
                            _cookieCollection = cookieCollection;
                        }
            
                        public bool ContainsPrefix(string prefix)
                        {
                            return _cookieCollection[prefix] != null;
                        }
            
                        public ValueProviderResult GetValue(string key)
                        {
                            HttpCookie cookie = _cookieCollection[key];
                            return cookie != null ? 
                                                  new ValueProviderResult(cookie.Value, 
                                                                          cookie.Value, 
                                                                          CultureInfo.CurrentUICulture) 
                                                  : null;
                        }
                    }
                }

All that needs to be done now is for the CookieValueProviderFactory to be added to the ValueProviderFactories collection via the Global.asax.  The framework will now search through five all five value providers in order to set parameters on action methods. Please be aware that this is done in order so if the value of isRegistered is set by the QueryStringValueProvider then the CookieValueProvider would not be used to set the value of isRegistered on the action method.

    public class MvcApplication : System.Web.HttpApplication
                {
                    public static void RegisterRoutes(RouteCollection routes)
                    {
                        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            
                        routes.MapRoute(
                            "Default", // Route name
                            "{controller}/{action}/{id}", // URL with parameters
                            new { controller = "Person", action = "Index", id = UrlParameter.Optional } 
                        );
                    }
            
                    protected void Application_Start()
                    {
                        AreaRegistration.RegisterAllAreas();
                        RegisterRoutes(RouteTable.Routes);
                        ValueProviderFactories.Factories.Add(new CookieValueProviderFactory());
                    }
                }

The implementation above will work with MVC 2.0 but not 1.0 as the ASP.NET MVC team changed how model binding is implemented between the framework versions.

Sample Code: Download

Tweet Me | Link To Facebook