I’m a big fan of pushing common concerns down into the infrastructure and framework of an application so that developers can easily leverage them.  One such concern that may come in a web application is displaying status messages, such as “Record Saved” or “There was a problem communicating with the database”.  My initial attempt at adding such functionality to RageFeed failed to encapsulate the concept of a status message, but after rethinking it and actually modeling it as an explicit action, I came up with an approach that I really like.

The Problem

It’s been a while since I’ve talked about RageFeed, my super-secret (probably will never see the light of day) social networking app, but I do manage to find time for it occasionally.  For a story I was working recently, I needed to display a status message to the user: whether or not their last operation succeeded or failed. 

Balsamiq Mockups rocks!

This is a fairly standard requirement.  One thing I wanted to do though was correctly implement the Post/Redirect/Get pattern.  To summarize, I wanted users to be able to bookmark the form, return to it, refresh the form, etc. without triggering duplicate form submissions.  At the same time though, I wanted to have a status message display on the page, but only immediately after the user submitted the form.  If the user refreshed the page again, the status message should disappear. 

This seemingly tricky requirement is actually quite easy to implement using ASP.NET MVC’s TempData container.  Craig Stuntz has a good post on this poorly-named container.  In a nutshell, it’s a session-baked store that persists its contents across exactly one round-trip to the server.  This makes it ideal for passing data between requests in a Post/Redirect/Get scenario. 

First attempt – TempData

My initial attempt at implementing status messages for RageFeed worked as follows.  First, I added the message to TempData after the post-back, then performed a redirect back to the form:

[HttpPost]
[BindCurrentUserToParam("currentUser")]
public ActionResult Stalk([Bind(Exclude = "Id")]User currentUser, StalkForm stalkForm)
{
    if (!ModelState.IsValid)
    {
        return View();
    }

    var reply = _bus.RequestReply<StalkUserRequest, StalkUserReply>(
        new StalkUserRequest {RequestingUser = currentUser, TargetUsername = stalkForm.Username});

    if (!reply.Succeeded)
    {
        ModelState.AddModelError("Username", GetErrorMessageFor(reply.FailureReason));
        return View();
    }

    TempData.Add("SuccessMessage", string.Format("You are now stalking {0}!", stalkForm.Username));

    return this.RedirectToAction(c => c.Stalk());
}

And here’s how the TempData was handled in the view:

<% if (TempData["SuccessMessage"] != null) { %>
<div class="success-message">
    <%=TempData["SuccessMessage"] %>
</div>
<%} %>
<h1>Stalk Your Friends</h1>
<%using (Html.BeginValidatedForm()) { %>

There were definitely some problems with this approach though.  First, it was not reusable at all. I would have had to reimplement this functionality on any other pages that required status messages.  It was also loosely-typed and used a magic string, neither of which are optimal from a maintenance point of view.  Finally, the code did not clearly express it’s intent.  Someone reading the code may see the message being placed in TempData, but without knowledge of the system, they wouldn’t really know what that’s supposed to do.  The concept of displaying a status message after redirecting is very weakly modeled. 

Final solution – custom view result

I needed to do a few things to overcome these limitations.  First, I had to bake the concept of a status message deeper into the application framework.  I also had to make leveraging the concept easier, and make the code’s intent clearer, which I did by making the action (redirecting with a status message) explicit.  The final controller code looks like this:

[HttpPost]
[BindCurrentUserToParam("currentUser")]
public ActionResult Stalk([Bind(Exclude = "Id")]User currentUser, StalkForm stalkForm)
{
    ...

    return RedirectWithStatusMessage<StalkerController>(c => c.Stalk(), 
            StatusMessage.Success("You are now stalking {0}!", stalkForm.Username));
}

The action now utilizes a custom action result. Under the hood it is still using TempData, but the developer utilizing the concept no longer needs to know or care about that: it’s all handled by the application framework.  For completeness, here’s what the custom action result looks like:

public class RedirectWithStatusMessageResult<T> : MvcContrib.ActionResults.RedirectToRouteResult<T> where T : Controller
{
    public StatusMessage Message { get; private set; }

    public RedirectWithStatusMessageResult(StatusMessage message, Expression<Action<T>> expression) : base(expression)
    {
        Message = message;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        context.Controller.TempData["StatusMessage"] = Message;

        base.ExecuteResult(context);
    }
}

Using a custom action result also makes the code a little easier to test and better separates the concerns, which is a nice bonus.

I also refactored the display of the status message out of the view and into the view master, making it available to any view in the entire application.

Final thoughts

The core problem with my initial solution was that it did not model the concept of redirecting with a status message explicitly.  Instead, it was clumsily specified using the weakly-typed TempData container.  After modeling the concept explicitly and pushing it into the application framework, I gained more readable code, code reuse, improved separation of concerns, and improved testability.  The moral of this story is to always model concepts explicitly, and to push any common concerns into the application framework where application developers can easily take advantage of them.