Click here to Skip to main content
15,882,209 members
Articles / Web Development / ASP.NET

Post/Redirect/Get User Notifications for ASP.NET MVC

Rate me:
Please Sign up or sign in to vote.
4.78/5 (5 votes)
5 May 2015MIT7 min read 30K   373   12   1
Easy cross request "flash" notifications for ASP.NET MVC with RedirectToAction() based on Twitter Bootstrap alerts

Introduction

In web application development, it is customary to follow a Post/Redirect/Get (PRG) pattern for pages containing forms. Creating, editing and updating data is usually performed using a POST request, followed by a redirect response to the user using the RedirectToAction() method. This however introduces a problem when you want to flash the user a message that his or her operation was (or wasn't) completed as requested. This article presents a solution to this problem for ASP.NET MVC called FlashMessage.

Background

The Problem with Notifications in PRG Applications

When a web application makes use of forms, a potential problem can occur when a user submits a form and attempts to refresh the page. The action executed in response to the form submission will be executed again, possibly leading to undesirable results. This problem is illustrated in the image below:

In order to circumvent this problem, the Post/Redirect/Get (PRG) pattern is typically employed. The client is always redirected directly after a form submission. In ASP.NET MVC, the controller method RedirectToAction() is typically used for this. If the users then refresh the page either using refresh or by using the back and forward buttons, the form is not submitted again. This is illustrated in the image below:

Solving the problem like this does however pose a problem if we want to notify the user after the form submission about the result of the operation. Because the GET request may be to another server in the cluster when using load balancing, it's not possible to just assume that you can store the notification locally.

Source of images: Wikipedia Post/Redirect/Get (PRG)

The Solution

This article presents a solution for this problem for ASP.NET MVC applications using cookies and/or the session state. It allows you to "flash" a message to the user before returning the redirect. The message is persisted to the next request where it is actually rendered.

Queuing Messages

Messages must be able to be queued for display at any point where an HTTP context is available. In addition, it would be nice to include full HTML when needed so that for example a link can be passed through the message. Also a few different types of messages would be handy to be able to style them as warnings, confirmations, etc.

In order to cover all these cases, each message is represented as follows:

C#
/// <summary>
/// The FlashMessageModel class represents an individual flash message.
/// </summary>
public class FlashMessageModel
{

    /// <summary>
    /// Gets / sets if the message contains raw HTML. If set to true, 
    /// the title will not be rendered and the contents of the message parameter will
    /// be written directly to the output.
    /// </summary>
    public bool IsHtml { get; set; }

    public string Title { get; set; }
    public string Message { get; set; }

    public FlashMessageType Type { get; set; }

    public FlashMessageModel()
    {
        IsHtml = false;
        Type = FlashMessageType.Info;
    }
}

/// <summary>
/// The FlashMessageType enum indicates the type of flash message.
/// </summary>
public enum FlashMessageType : byte
{
    Info,
    Warning,
    Danger,
    Confirmation
}

The IsHtml property indicates if the message content in the Message property should be rendered as raw HTML (watch out for HTML injection here). The Title property stores an optional title and the Type property holds one of the messages types as defined by the FlashMessageType enum.

In order to queue a message, the static FlashMessage.Queue() method was implemented. This method retrieves the current set of queued flash messages and appends the new message to the queue before storing the queue again.

In order to ease usage, a few shorthand methods are provided which directly queue various types of messages. These methods also accept a format string and variable arguments to prevent the programmer from having to write lengthy statements like: FlashMessage.Confirmation(string.Format("format...", args)) all the time.

Serializing the Messages

In order to keep the message size to a minimum and to reduce overhead, the messages are serialized using binary serialization. The BinaryReader and BinaryWriter classes are used to this end. While performance is not really an issue as the messages should be short anyway, the binary serialization should perform pretty well.

Storing the Messages

In order to storage the messages between one request to another, a 'transfer' method is required which takes care of transferring the data from the POST request to the GET request.

TempData

ASP.NET MVC provides every controller with a TempData dictionary for storing temporary data between requests. At the next request, its contents are typically discarded, which would initially suggest that it's a good fit for storing user notifications in our scenario. For the following reasons, I however chose not to use TempData:

  • TempData uses sessions behind the scene. I tend not to use sessions in most applications.
  • TempData requires access to the current controller which may not always be available, for example when not using ASP.NET MVC.
  • TempData by default always discards all data at the next request, while I would want the notifcations to be retained until they are acutally shown.

In order to work around these shortcomings, two transport methods are provided in this framework. The tranport providers inherit the IFlashMessageTransport interface.

Transport via Cookie

Because most messages will be short, I consider transporting the messages via cookies a viable option. Tranport via cookies is implemented by the FlashMessageCookieTransport class. This is the default transport for the framework as it should work in almost any scenario.

When using cookie transport, it is of utter importance to prevent the client from tampering with the cookie. The cookie is exposed to potentially harmful JavaScript on the client side and can thus be manipulated if not secured properly. In order to prevent this, the cookie is encoded using the System.Web.Security.MachineKey.Encode() method.

Transport via Session

Another option is to transport the messages via the session state, which is essentially the same what TempData does. The difference when using the session state directly is that the dependency with on the controller method is removed, making the code usable outside the ASP.NET MVC framework.

Transport via the session state is provided by the FlashMessageSessionTransport class. Note that when using session transport, you'll need to make sure that the session state storage is shared across all server processes.

Displaying Messages

When displaying the messages, the messages are retrieved from the transport provider and removed from the message store. Displaying them can be done using a custom PartialView, or using the included FlashMessageHtmlHelper class which extends the ASP.NET HtmlHelper class.

The typical manner to show the messages would be to include a call to Html.RenderFlashMessages() at the top of your page in the master or layout template like this:

HTML
<div class="container">
    @Html.RenderFlashMessages()
    @RenderBody()
</div>

The included FlashMessageHtmlHelper class formats the messages for the widely used Twitter Bootstrap framework.

Using the Code

  1. First of all, you'll need to add references to ASP.NET MVC and this package FlashMessage. Also make sure that the Web.Vereyon namespace is imported in the relevant code files.
  2. Next, you need to make sure that the messages are rendered properly. The most obvious manner would be to include the following code in your Razor layout template before the RenderBody() call:
HTML
@Html.RenderFlashMessages()

Make sure that you also include the necessary Twitter Bootstrap stylesheets, or write your own.

3. Now queue messages before your redirects using one of the following calls:

C#
FlashMessage.Info("Your informational message");
FlashMessage.Confirmation("Your confirmation message");
FlashMessage.Warning("Your warning message");
FlashMessage.Danger("Your danger alert");

4. Done! Your code should now look something like the following:

C#
[HttpPost]
public ActionResult Save()
{

    // Do you usual controller action stuff here.

    // Redirect the user.
    FlashMessage.Confirmation("The entity was saved.");
    return RedirectToAction("View", new { Id = entity.id });
}

Advanced Options

Each of the presented methods to queue a message internally uses the FlashMessage.Queue() method which is defined as follows:

C#
FlashMessage.Queue(string message, string title, FlashMessageType messageType, bool isHtml);

The FlashMessage.Queue() method allows one to specify values for each of the FlashMessage classes properties.

In order to reduce the length of the code required to schedule a message, every instance of the standard message scheduling methods also accepts a format string and a variable argument list. This prevents the user from having to include verbose String.Format() calls.

C#
FlashMessage.Info(string format, params object[] args);

Custom Message Rendering (Optional)

In case you do not want to use the Twitter Bootstrap styles messages from the FlashMessageHtmlHelper, then you need to write your own message rendering code. A good way to start doing this is including the following code in your Razor layout template:

C#
@foreach(var message in FlashMessage.Retrieve(Html.ViewContext.HttpContext))
{
    Html.RenderPartial("FlashMessage", message);
}

The call to FlashMessage.Retrieve() retrieves all the queued flash messages and removes them from the queue. You should create a partial view called FlashMessage which renders each of the FlashMessageModels.

Configure Transport (Optional)

In case you want to configure the transport being used, the best place to do this would be in the application startup. The transport provider used is exposed using the static FlashMessage.Transport property. Setting a different transport is done as follows:

C#
FlashMessage.Transport = new FlashMessageSessionTransport();

Points of Interest

The source contains a test project which defines some unit tests to verify the functionality of the code. While the test coverage is not super extensive, some serialization and deserialization as well as the various transports providers are tested. Also some likely scenario's such as missing or malformed cookie data are tested.

History

Find the latest version at https://github.com/Vereyon/FlashMessage

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Netherlands Netherlands
Developer at AlertA contractbeheer.

Comments and Discussions

 
GeneralNice article Pin
devcovato9-May-15 21:22
devcovato9-May-15 21:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.