Please read this post for my reasons behind this article series.
Using flash messages between web requests
Ok I admit, this is not entirely a C# vs. Ruby issue. It is more Ruby On Rails done with MVC3. But it uses some of the dynamic stuff from C#.
In Rails you can push messages between requests by using a special flash component:
controller UserController < ApplicationController
def Login
user = (try-to-login-user)
if not user.LoggedIn?
flash[:error] = “Invalid credentials”
redirect_to :controller => “LoginForm”
end
end
…
This will use sessions to carry the error-message into the next request. You can now test for the flash-message in LoginForm's view:
Login
<% if flash[:error] %><%=flash[:error]%><% end %>
After this request, the flash message is erased automatically.
The same thing can be made in .NET by using a combination of the dictionary TempData" and C#'s extension methods. And we can formalize it a bit more by using an enum to control how many different message types, that we will accept.
Create a file called "FlashHelper.cs" with the following content:
using System;
using System.Collections.Generic;
using System.Text;
namespace System.Web.Mvc
{
public enum FlashType
{
Info,
Warning,
Error
}
public class FlashData
{
public FlashType Key { get; set; }
public string Message { get; set; }
}
public static class FlashHelpers
{
public static void Flash(this Controller controller, FlashType key, string message)
{
if (controller.TempData["flash"] == null)
controller.TempData["flash"] = new List<FlashData>();
var flashList = controller.TempData["flash"] as List<FlashData>;
flashList.Add(new FlashData {Key = key, Message = message});
}
public static MvcHtmlString Flash(this HtmlHelper helper)
{
var sb = new StringBuilder();
if (helper.ViewContext.TempData["flash"] != null)
{
foreach (FlashData flash in helper.ViewContext.TempData["flash"] as List<FlashData>)
{
sb.AppendLine(string.Format("<div class=\"flash {0}\">", flash.Key.ToString().ToLower()));
sb.AppendLine(flash.Message);
sb.AppendLine("</div>");
}
}
return MvcHtmlString.Create(sb.ToString());
}
}
}
What I am doing here is, that I extend the base MVC controller with a new method called "Flash()". You call this in your controller to set a new message, supplying the type of message and the message-string itself.
The same goes for extending the Html helper. I define a method, that will print out all added messages, each of them wrapped into their own DIV tag with classes set appropiate of the message type.
So now we can write a C# MVC 3 example like the Rails one above.
The controller:
public class UserController : Controller
{
public RedirectToRouteResult DoLogin(string username, string password)
{
var user = (try-to-login-user);
if (!user.LoggedIn()) {
this.Flash(FlashType.Error, "Invalid credentials");
return RedirectToAction("LoginForm");
}
...
And in your _layout.cshtml view:
@Html.Flash() @RenderBody()
If this example is run, and the user tries to login with invalid credentials, the @Html.Flash() call will render:
Invalid credentials...
which you can then style with CSS.