ASP.NET MVC Tip #15 – Pass Browser Cookies and Server Variables as Action Parameters
In this tip, I demonstrate how you can pass browser cookies and HTTP server variables to controller action methods in the same way as you can pass form and query string parameters.
Imagine that you make the following browser request against an ASP.NET MVC web application:
When you make this request, by default, the ASP.NET MVC framework will invoke an action named Index() exposed by a class named ProductController. There is a class in the ASP.NET MVC framework, named the ControllerActionInvoker class, which is responsible for invoking a controller action in response to a browser request.
The ControllerActionInvoker class has several responsibilities. This class must find a method that matches the method being invoked. Furthermore, the ControllerActionInvoker is responsible for building the list of parameters that are passed to the invoked method.
Imagine, for example, that the Details() controller action in Listing 1 is being invoked:
Listing 1 – ProductController.vb (VB.NET)
Public Class ProductController
Inherits System.Web.Mvc.Controller
Function Details(ByVal productId As Integer)
Return View()
End Function
End Class
Listing 1 – ProductController.cs (C#)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Tip15.Controllers
{
public class ProductController : Controller
{
public ActionResult Details(int productId)
{
return View();
}
}
}
The Details() controller action has a single parameter named productId. The ControllerActionInvoker attempts to retrieve the value of this parameter from 3 places:
1. Explicit Extra Parameters Passed to InvokeAction()
2. Values from the Route Data
3. Request Values
First, an attempt is made to retrieve the value of the productId parameter from the parameters passed to the ControllerActionInvoker.InvokeAction() method. This method is called by the Controller.Execute() method. The default controller does not pass any additional parameters. If you create your own controller, then you can pass any parameters that you please.
Second, an attempt is made to retrieve the value of the productId parameter from the route data. The route data might contain a default value for productId. Or, your route table might map a segment of a URL to the productId. The default route table does not map anything to the productId parameter.
Finally, an attempt is made to retrieve the value of productId from the HttpRequest object (HttpContext.Request). The HttpRequest object represents Query String, Form, Cookie, and Server Variable items (in that order). This means that an attempt will be made to retrieve the value of productId from all of these sources.
If the ControllerActionInvoker cannot retrieve the value of a parameter from these three places then the ControllerActionInvoker checks whether or not the parameter can be null. If a parameter can be null, then the ActionInvoker passes a null value. Otherwise, the ControllerActionInvoker throws an InvalidOperationException (see Figure 1).
Figure 1 – Can’t Match Parameters
Until I investigated the ControllerActionInvoker class, I did not realize that controller action parameters could be retrieved from browser cookies and HTTP server variables. Consider the Index() action exposed by the HomeController in Listing 2.
Listing 2 – HomeController.vb (VB.NET)
Public Class HomeController
Inherits System.Web.Mvc.Controller
Public Function Index(ByVal HTTP_USER_AGENT As String, ByVal myCookie As String)
ViewData("HTTP_USER_AGENT") = HTTP_USER_AGENT
ViewData("myCookie") = myCookie
Return View()
End Function
Public Function CreateCookie(ByVal cookieValue As String)
Dim newCookie As New HttpCookie("myCookie", cookieValue)
newCookie.Expires = DateTime.Now.AddDays(10)
Response.AppendCookie(newCookie)
Return RedirectToAction("Index")
End Function
End Class
Listing 2 – HomeController.cs (C#)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Tip15.Controllers
{
public class HomeController : Controller
{
public ActionResult Index(string HTTP_USER_AGENT, string myCookie)
{
ViewData["HTTP_USER_AGENT"] = HTTP_USER_AGENT;
ViewData["myCookie"] = myCookie;
return View();
}
public ActionResult CreateCookie(string cookieValue)
{
var newCookie = new HttpCookie("myCookie", cookieValue);
newCookie.Expires = DateTime.Now.AddDays(10);
Response.AppendCookie(newCookie);
return RedirectToAction("Index");
}
}
}
The Index() method accepts two parameters named HTTP_USER_AGENT and myCookie. Because the parameter name HTTP_USER_AGENT corresponds to a server variable, this parameter gets a value automatically. Because myCookie corresponds to a browser cookie, this parameter also has a value (if the cookie is set). When the Index() method is invoked, you get the view in Figure 2.
Figure 2 – The Index View
The fact that you can pass server variables and cookies to controller actions can be useful in a variety of different scenarios. For example, you might want to return different content depending on the type of browser being used to make the request. You can take advantage of the HTTP_USER_AGENT server variable in your action method to detect the type of browser and return different content. Or, you might want to return messages in different spoken languages depending on the user's browser language settings.
Passing cookie parameters also can be useful. You could store a user’s preferences in a cookie and retrieve those preferences in an action method automatically. For example, you could store a user’s nick name in a browser cookie and retrieve that nick name in every controller action automatically.