Since I primarily work on internal business applications, I often find myself writing the same kinds of code multiple times. One of the most common situations is a "status message" or some kind of display to the user that lets them know that something successfully happened, or there's some kind of error, or warning, or just a friendly reminder. Messages like these:

User John Smith was successfully added to group "Admins"
Please ensure that Birth Date has a valid date.
The user's first name cannot be blank
You have 30 seconds to stop the missile launch. Good luck!

Well, okay, maybe we don't use that last one too often...

Point is, these kinds of messages are commonplace in my apps, and I'll be they're commonplace in many others as well. My team and I worked out a set of .NET methods and extensions to render these with as little clutter as possible, and I hope that sharing them with you, dear readers, will make your apps just that little bit more readable too.

The Sample Project

Check out the sample project for this post; you can run it locally to see the different kinds of messages that can be rendered.

exceptionnotfound/FlashMessageCoreDemo
Contribute to exceptionnotfound/FlashMessageCoreDemo development by creating an account on GitHub.

Quick Notes

The CSS used in this post is from Bootstrap; you will need to modify it to work with whatever CSS framework you are using.

Also, this post is set up using .NET 5.0 MVC, though it could easily be refactored to work in Razor Pages.

Getting Started: The Basic Message

The two message types share a lot of their implementation. In fact, the goal for both implementations is to output the same HTML markup, which looks like this:

<div class="row">
    <div class="col-lg">
        <div class="rounded pg-3 alert-success">
            User John Smith was successfully added to group "Admins"
        </div>
    </div>
</div>

Message Types

First, let's create an enumeration for the different kinds of messages:

public enum FlashMessageType
{
    Success,
    Warning,
    Error,
    Info
}

IHtmlContent and the Common Extension Method

.NET 5.0 includes IHtmlContent, which represents HTML that needs to be rendered to a view. We will build an extension method that returns IHtmlContent and renders the message given to it.

Because the CSS class might change with any given call to this method, we will pass a string for the unique CSS class:

public static class MessageExtensions
{
    private static IHtmlContent StatusMessage(string message, 
                                              string cssClass)
    {
        // First, build the innermost div tag, 
        // the one that displays the message
        TagBuilder display = new TagBuilder("div");
        display.AddCssClass("rounded p-lg-3");
        display.AddCssClass(cssClass);
        display.InnerHtml.Append(message);

        // Now, build the wrapping column
        TagBuilder columns = new TagBuilder("div");
        columns.AddCssClass("col-lg");
        columns.InnerHtml.AppendHtml(display);

        // Finally, build the wrapping row
        TagBuilder row = new TagBuilder("div");
        row.AddCssClass("row");
        row.InnerHtml.AppendHtml(columns);

        // TagBuilder implements IHtmlContent
        // so we can just return that instance.
        return row;
    }
}

At this point, we can do two different implementations of these messages, which will be used in different circumstances.

Implementation #1 - Flash Messages

The first implementation of these messages is what we termed "flash messages". These messages result from situations such as

  • User input validation fails
  • An item is changed on the server, or changes are saved.

These are "short-lived" messages, and we only want to display them to the user for a little while. Lucky for us, .NET 5 MVC includes an object called TempData, which holds values only until they are read by a different request. Once they are read, they disappear. We will use TempData to store our flash messages.

TempData Keys

Our message text and type will be stored in TempData, and we access information in that object using a key. We need to define two keys: one for the message text, and one for the message type.

public class Constants
{
    public const string FlashMessageKey = "FlashMessage";
    public const string FlashMessageTypeKey = "FlashMessageType";
}

You may want to use more specific keys; they are kept simple here for readability.

A New Extension Method

We also need another extensions method, one that reads the message and type out of TempData and renders the correct HTML, but only if the message actually exists. Said method (which we will call FlashMessage()) looks like this:

public static class MessageExtensions
{
    public static IHtmlContent FlashMessage(this ITempDataDictionary tempData)
    {
        if (tempData[Constants.FlashMessageKey] != null)
        {
            string message = tempData[Constants.FlashMessageKey].ToString();
            string type = tempData[Constants.FlashMessageTypeKey].ToString();
            return StatusMessage(message, type);
        }
        else return HtmlString.Empty;
    }

    private static IHtmlContent StatusMessage(string message, 
                                              string cssClass)
    {
        //See earlier implementation
    }
}

This method:

  1. Reads the message text and type out of TempData if they exist AND
  2. Calls the StatusMessage() method to return renderable HTML.

Note that we still have not written code that sets either the text or type. That's the part we need to do next. But where does it go?

BaseController

Controller objects in .NET 5.0 MVC keep a reference to an ITempDataDictionary object, which we will need to access in order to set the message text and type. To do this, my team created a BaseController object, from which all other controllers will inherit.

public class BaseController : Controller { /*implementation*/ }

Within BaseController, there's a method which takes the message and an instance of FlashMessageType and uses the keys from earlier to set those items into TempData:

public class BaseController : Controller
{
    public void SetFlashMessage(string message, FlashMessageType type)
    {
        TempData[Constants.FlashMessageKey] = message;

        string messageCSS = "";

        switch (type)
        {
            case FlashMessageType.Success:
                messageCSS += "alert-success";
                break;

            case FlashMessageType.Warning:
                messageCSS += "alert-warning";
                break;

            case FlashMessageType.Error:
                messageCSS += "alert-danger";
                break;

            case FlashMessageType.Info:
                messageCSS += "alert-info";
                break;
        }

        TempData[Constants.FlashMessageTypeKey] = messageCSS;
    }
}

Then, in our other controllers, we can call this method like so:

public IActionResult SomeAction()
{
    SetFlashMessage("There was an error!", FlashMessageType.Error);
    //Rest of implementation
}

Finally, on our views, we can render this message by calling TempData.FlashMessage():

@{
    ViewData["Title"] = "Flash Message";
}

<!-- Rest of implementation -->
@TempData.FlashMessage()

How It Looks

If you run the sample app, you can show the page in the screenshots below, and see how different messages and types would look to the user.

If you then refresh the page, you'll see that the flash message disappears:

Implementation #2 - Status Messages

The second kind of message we termed "status" messages. These are messages that are displayed in situations such as:

  • A set of items was expected, but was not found.
  • A table has no data.

In short, these are messages that are intended to be displayed whenever a set of data or a particular display was expected, but cannot be rendered for whatever reason. These are "long-lived" errors, in that they could be displayed for a longer period of time.

New Extension Methods

Because we want to render the status messages from MVC views, we can create a set of extension methods that we can call from the views:

public static class MessageExtensions
{
    public static IHtmlContent ErrorMessage<TModel>(this IHtmlHelper<TModel> helper, string message)
    {
        return StatusMessage(message, "alert-danger");
    }

    public static IHtmlContent SuccessMessage<TModel>(this IHtmlHelper<TModel> helper, string message)
    {
        return StatusMessage(message, "alert-success");
    }

    public static IHtmlContent WarningMessage<TModel>(this IHtmlHelper<TModel> helper, string message)
    {
        return StatusMessage(message, "alert-warning");
    }

    public static IHtmlContent InfoMessage<TModel>(this IHtmlHelper<TModel> helper, string message)
    {
        return StatusMessage(message, "alert-info");
    }
}

We can then call them in views like so:

@Html.SuccessMessage(message)
@Html.WarningMessage(message)
@Html.ErrorMessage(message)
@Html.InfoMessage(message)

In the sample app, you can see how a single message might appear differently using each of these extensions.

Summary

These message implementations have made it easier for my team to avoid repeating code, and it's now super easy to add them into views where they did not exist before. A few extension methods, a BaseController, and some TempData goes a long way.

Do you like this implementation? Hate it? Somewhere in between? Think it can be improved? I wanna know! Sound off in the comments below.

Happy Coding!