Build a FlashMessenger system in ASP.NET MVC

Build a FlashMessenger system in ASP.NET MVC

Usually, I’m using PHP for web projects, but currently I’m working on an application using ASP.NET’s MVC framework. In many frameworks (e.g. Zend Framework or CakePHP) there is some kind of messaging system (flash messages), which allows to save a message to the session and to display it on the next request. This is useful in many situations, but mostly used after saving forms. So for example you recieve a POST request to create a new record, you save the record, redirect the user to the overview page and inform him about successful creation through a flash message which you saved before doing the redirect. As I didn’t find a way how to achieve this in ASP.NET MVC, I started to build an own solution. The result is a combination of a base controller, an action filter and a flashmessenger object which is stored in the session. Please note that I’m not too familiar neither with ASP.NET nor with C#/.NET in general, so if there are things to improve please feel free to correct me.

I’m working with the default application which is generated when creating a new ASP.NET MVC project in Visual Studio.

FlashMessenger

To store all the messages, I created a class called FlashMessenger which is responsible to store the messages. I created a new folder LibrarySession and saved the class as FlashMessenger.cs. The class allows saving of messages to 3 namespaces (success, notice, error) by default – to add more namespaces just edit the enum.

using System;
using System.Collections;
using System.Collections.Generic;

namespace MyProject.Library.Session
{
    public class FlashMessenger
    {
        public enum MessageType
        {
            Success,
            Notice,
            Error
        };

        private Dictionary<MessageType, Queue<string>> _messages;
        public Dictionary<MessageType, Queue<string>> Messages
        {
            get { return _messages; }
            private set { _messages = value; }
        }

        public FlashMessenger()
        {
            Messages = new Dictionary<MessageType, Queue<string>>();
            foreach (MessageType type in Enum.GetValues(typeof(MessageType)))
            {
                Messages[type] = new Queue<string>();
            }
        }
    }
}

BaseController

The next element is the BaseController which initializes the FlashMessenger and implements methods to easily create new messages. All my controllers inherit from this controller, so I can use messaging everywhere. The [Messaging] attribute on the class level ensures all actions use the MessagingAttribute we’ll create in the next step. I created the controller as BaseController.cs in LibraryControllers.

using System;
using System.Web;
using System.Web.Mvc;
using MyProject.Library.Session;

namespace MyProject.Library.Controllers
{
    [Messaging]
    public class BaseController : Controller
    {
        protected override void Initialize(System.Web.Routing.RequestContext requestContext)
        {
        base.Initialize(requestContext);
            this.InitializeFlashMessenger();
        }        

        # region FlashMessenger
        public FlashMessenger Messenger
        {
            get { return (FlashMessenger)Session["FlashMessenger"]; }
            private set { }
        }

        protected void InitializeFlashMessenger()
        {
            if (Messenger == null)
                Session["FlashMessenger"] = new FlashMessenger();
        }

        public void FlashMessage(string message)
        {
            FlashMessage(message, FlashMessenger.MessageType.Success);
        }

        public void FlashMessage(string message, FlashMessenger.MessageType type)
        {
            Messenger.Messages[type].Enqueue(message);
        }
        # endregion
    }
}

This allows us to use the FlashMessaging system in every controller which inherits from BaseController. For example:

public ActionResult Foo()
{
    FlashMessage("You have been redirected to Bar from Foo", FlashMessenger.MessageType.Notice);
    return RedirectToAction("Bar", "Home");
}

MessagingAttribute

Our messaging system is set up, but we still need to display our messages. To achieve this, I created an action filter called MessagingAttribute which injects the messages into the ViewData object. The output are simple unordered lists which get the MessageType as class name. As the attribute is set up on the class level of BaseController, the filter gets executed on every action and messages are available in every view. I saved the filter as MessagingAttribute.cs in LibraryControllersFilters.

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MyProject.Library.Session;

namespace MyProject.Library.Controllers
{
    public class MessagingAttribute : ActionFilterAttribute, IActionFilter
    {
        void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
        {
            FlashMessenger Messenger = (FlashMessenger) filterContext.HttpContext.Session["FlashMessenger"];
            StringBuilder output = new StringBuilder();

            foreach (KeyValuePair<FlashMessenger.MessageType, Queue<string>> kvp in Messenger.Messages)
            {
                if (kvp.Value.Count > 0)
                {
                    output.AppendLine("<ul class="" + kvp.Key.ToString() + "">");

                    while(kvp.Value.Count > 0)
                    {
                        output.AppendLine("<li>" + kvp.Value.Dequeue() + "</li>");
                    }

                    output.AppendLine("</ul>n");
                }
            }

            filterContext.Controller.ViewData["FlashMessenger"] = output;
        }
    }
}

View and CSS

To display the messages in your application, edit the following. In your Site.master, display the output from the action filter:

<div id="main">           
    <div id="messages">
        <%= ViewData["FlashMessenger"] %>
    </div>            

    <asp:ContentPlaceHolder ID="MainContent" runat="server" />

    <div id="footer">
    </div>
</div>

In your Site.css, add some styling for your messages:

#messages {
    margin-bottom: 2em;
}

#messages ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

#messages ul li {
    display: block;
    margin: .75em 0;
    padding: 10px;
    border: 2px solid #ddd;
    background-color: #eee;
    color: #222;
}

#messages ul.Notice li {
    background: #FFF6BF;
    color: #817134;
    border-color: #FFD324;
}

#messages ul.Success li {
    background: #E6EFC2;
    color: #529214;
    border-color: #C6D880;
}

#messages ul.Error li {
    background: #FBE3E4;
    color: #D12F19;
    border-color: #FBC2C4;
}

Example usage

As an example, we modify the AccountController to show a nice confirmation message on login and logout.

First of all, import the Library.Session namespace and let the controller inherit from BaseController.

@@ -8,11 +8,13 @@ using System.Web.Mvc;
 using System.Web.Security;
 using System.Web.UI;
 using MyProject.Models;
+using MyProject.Library.Controllers;
+using MyProject.Library.Session;

 namespace MyProject.Controllers
 {
     [HandleError]
-    public class AccountController
+    public class AccountController : BaseController
     {

         // This constructor is used by the MVC framework to instantiate the controller using

Add a flash message to your logon action:

@@ -63,6 +63,7 @@ namespace MyProject.Controllers
             }

             FormsAuth.SignIn(userName, rememberMe);
+            FlashMessage("Logged on successfully", FlashMessenger.MessageType.Success);^M

             if (!String.IsNullOrEmpty(returnUrl))
             {

And to your logoff action:

@@ -78,6 +78,7 @@ namespace MyProject.Controllers
         public ActionResult LogOff()
         {
             FormsAuth.SignOut();
+            FlashMessage("Logged off successfully", FlashMessenger.MessageType.Success);^M
             return RedirectToAction("Index", "Home");
         }

Conclusion

So far the system is finished and allows easy flashing of messages between page requests. However, I’m not completeley happy with the solution of having to use a base controller to initialize and use the messaging system. If anyone knows a better solution, please let me know.

  • Pingback: Daily Tech Links - June 2, 2009 - Sanjeev Agarwal()

  • justin

    TempData is built into asp.net mvc and can do this fairly easily.

  • justin

    TempData is built into asp.net mvc and can do this fairly easily.

  • Joe

    Instead of writing a base that your controllers must inherit from, simply add extensions methods to Controller to add and retrieve messages from state.

    As another poster already pointed out, you can use the inbuilt TempData object instead of rolling your own messages container and storing it in session.

  • Joe

    Instead of writing a base that your controllers must inherit from, simply add extensions methods to Controller to add and retrieve messages from state.

    As another poster already pointed out, you can use the inbuilt TempData object instead of rolling your own messages container and storing it in session.

  • Gleb

    dude, your code smells.

    • Thank you for constructive criticism…you may have read the introduction which mentions that this was my first contact with ASP.NET.

      — Mathias

  • Gleb

    dude, your code smells.

    • Thank you for constructive criticism…you may have read the introduction which mentions that this was my first contact with ASP.NET.

      — Mathias

  • Mathias – don’t be discouraged by a few rude comments. Not all of the ASP.NET community is so harsh. You took the time to try and help others – to me that’s great!

    Better luck next time!

  • Mathias – don’t be discouraged by a few rude comments. Not all of the ASP.NET community is so harsh. You took the time to try and help others – to me that’s great!

    Better luck next time!

  • Marco

    Thank you for a great Post.

    Allthough there might be a better way(which people above seems to point to) this was a great informative post, which helped me a lot(also coming from PHP where Flash Session are really nice).

    Now i have what i needed, and i can look into TempData(people above are welcome to make examples).

    Thumbs up! :)

  • Marco

    Thank you for a great Post.

    Allthough there might be a better way(which people above seems to point to) this was a great informative post, which helped me a lot(also coming from PHP where Flash Session are really nice).

    Now i have what i needed, and i can look into TempData(people above are welcome to make examples).

    Thumbs up! :)

  • will

    Found this really useful and comprehensive… many thanks

  • will

    Found this really useful and comprehensive… many thanks

  • Nice solution !!!
    I’m thinking how i can use this between ajax requests…

    Any sugestions ???

  • Nice solution !!!
    I’m thinking how i can use this between ajax requests…

    Any sugestions ???

  • Marlon

    Use tempdata

    <BASE CONTROLE================================
    public class BaseController : Controller
    {
    protected static readonly ILog Log = LogManager.GetLogger(typeof(BaseController));

    public enum MessageType
    {
    Success,
    Notice,
    Error
    };

    public void FlashMessage(String Message, MessageType Type)
    {
    StringBuilder output = new StringBuilder();
    output.AppendLine("”);
    output.AppendLine(“” + Message + “”);
    output.AppendLine(“”);
    TempData[“FlashMessenger”] = output.ToString();
    }

    }

    <VIEW EX.: Site.Master================================

    <Sample================================
    public ActionResult Index()
    {
    FlashMessage("Teste", MessageType.Success);
    return View();
    }

  • Marlon

    Use tempdata

    <BASE CONTROLE================================
    public class BaseController : Controller
    {
    protected static readonly ILog Log = LogManager.GetLogger(typeof(BaseController));

    public enum MessageType
    {
    Success,
    Notice,
    Error
    };

    public void FlashMessage(String Message, MessageType Type)
    {
    StringBuilder output = new StringBuilder();
    output.AppendLine("”);
    output.AppendLine(“” + Message + “”);
    output.AppendLine(“”);
    TempData[“FlashMessenger”] = output.ToString();
    }

    }

    <VIEW EX.: Site.Master================================

    <Sample================================
    public ActionResult Index()
    {
    FlashMessage("Teste", MessageType.Success);
    return View();
    }

  • CPank

    This is great, was looking for the exact same thing from Cake – thanks for the post.

    public void FlashMessage(String Message, MessageType Type)
    {
    StringBuilder output = new StringBuilder();
    output.AppendLine(“”);
    output.AppendLine(“” + Message + “”);
    output.AppendLine(“”);
    TempData[“FlashMessenger”] = output.ToString();
    }

  • CPank

    This is great, was looking for the exact same thing from Cake – thanks for the post.

    public void FlashMessage(String Message, MessageType Type)
    {
    StringBuilder output = new StringBuilder();
    output.AppendLine(“”);
    output.AppendLine(“” + Message + “”);
    output.AppendLine(“”);
    TempData[“FlashMessenger”] = output.ToString();
    }

  • Afshin Mehrabani

    Hi,

    Thanks for your good article.
    When i have a POST request (and i have my page Model) why you use Session for collecting messages? I can use and collect messages in Model…

    Thanks,
    Afshin