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:

http://localhost/Product/Index

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

clip_image002

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

clip_image004

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.

Download the Code

4 Comments

  • Hmm, I didn't look into it much, but is that -safe-? I mean, if the variables in the index function are filled up automatically... it would be ok if it was only one type (all cookies, or all server variables), but since you can mix and match, whats to present me from forging a request with a cookie with the same name as the server variables?

    I mean, it is already possible to forge anything client-related, for obvious reason... but forging info that potentially come from the server? That seems...awkward...

    (again, keep in mind this is just my first reaction, maybe if I think it through I'll realise what I just said is totally stupid :) )

  • So... how could I link to a controller that accepts server variables via an Html.ActionLink. It would get a compile error if you don't specify all the parameters... yet that would be exactly what I want to do... not specify the parameters and have them filled in for me.

  • Exactly as tf124 said... how do you combine these techniques with something like Html.BuildUrlFromExpression() or Html.ActionLink() ?

    These methods won't compile unless you specify their arguments in the method call - so how can you refer to them within pages and still use the automatic parameter population features?

    Great article, though - thanks!

  • Asp net mvc tip 15 pass browser cookies and server variables as action parameters.. Not so bad :)

Comments have been disabled for this content.