Controller TempData in ASP.NET MVC 3

I found an excellent article today over on Stack Overflow on how to pass arbitrary data between action methods. The solutions posted by Ryan Tofteland and Tomas Lycken both argue for using a feature of controllers I hadn’t come across before; the TempData property.

TempData allows you to store arbitrary data in one action method and retrieve it in another. It’s really no different to setting a Session variable, in fact this is exactly what happens under the hood – although this default behaviour can be changed to use cookies.

To demonstrate, I’m going to implement a custom Authorize attribute where I can set an error message, and have that message displayed on the login page (if the user wasn’t logged-in).

Imagine we’re creating a webpage for a restaurant where people can book tables online, but must be logged-in to do so. We have a Dinner controller with a BookDinner action method. To ensure users are logged-in, the BookDinner method has the Authorize attribute, but to make this work the way we want we’re going to need something more. If a user tries to open this page without logging-in, I’d like a friendly error message to be shown on the Logon page. We need our own attribute, let’s call it AuthorizeWarning:

using System.Web.Mvc;
using MyWebsite.Helpers;

namespace MyWebsite.Controllers
{
    public class DinnerController : Controller
    {
        [AuthorizeWarning(Message="You must log-on before booking a dinner")]
        public ActionResult BookDinner()
        {
            return View();
        }
    }
}

And now to create it – the attribute must inherit from AuthorizeAttribute since we want it to behave in the same way. In order to hook in our code we override the HandleUnauthorizedRequest method and pass our error message into the Controller.TempData collection:

namespace MyWebsite.Helpers
{
    public class AuthorizeWarningAttribute : System.Web.Mvc.AuthorizeAttribute
    {
        public string Message { get; set; }

        public AuthorizeWarningAttribute()
            : base()
        { }

        protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
        {
            base.HandleUnauthorizedRequest(filterContext);
            filterContext.Controller.TempData["ReferrerMessage"] = this.Message;
        }
    }
}

Now that the error message is stored it needs to be retrieved. This requires a small change to the LogOn action to pass the temp data from the controller into the view model. Since the LogOn view doesn’t have a dedicated model, the ViewBag will suffice:

namespace MyWebsite.Controllers
{
    public class AccountController : Controller
    {
        public ActionResult LogOn()
        {
            ViewBag.ReferrerMessage = TempData["ReferrerMessage"];
            return View();
        }

        /* the rest of account controller */
    }
}

Now that the view model has the message, the LogOn view needs to display it:

<h2>Log On</h2>
@if (!string.IsNullOrWhiteSpace(ViewBag.ReferrerMessage))
{
    <p><strong>@ViewBag.ReferrerMessage</strong></p>
}
<p>
    Please enter your username and password. @Html.ActionLink("Register", "Register") if you don't have an account.
</p>
<!-- The rest of the logon view -->

Accessing the url “/dinners/bookdinner” without being logged-in will produce:

About these ads
This entry was posted in Tips and Tricks and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s