Category Archives: ASP.NET MVC

MvcPaging 1.0.2

A new version of the MvcPaging component is available via NuGet with the following changes:

So the core library hasn’t changed much, and in all honesty, I think it’s pretty much done. Please leave a reply if you think otherwise.

What did change substantially is the sample application. It was still very much ASP.NET 1.0 with WebForms views. This is now a proper ASP.NET MVC 3 application with Razor views and the default ASP.NET MVC 4.0 template for look & feel.

 Screenshot

The sample application can be found at GitHub.

Custom Model Binders and Request Validation

In ASP.NET MVC you can create your own model binders to control the way that models are constructed from an HTTP request. For example, if you don’t want any whitespace characters in your model, you can create a custom model binder that trims all whitespace characters when constructing  a model object. You only have to implement the IModelBinder interface and its BindModel method in a class and register it during application startup.

Getting the value from the HTTP request

There are many examples online of how to build a custom model binder. In all these examples you can find a common way of getting a value from the HTTP request:

public class MyModelBinder: IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (valueProviderResult != null)
        {
            var theValue = valueProviderResult.AttemptedValue;

            // etc...
        }
    }
}

The code above works nicely and you get protection for potential dangerous request values out of the box. Now let’s assume that we want some potential dangerous values like HTML or XML tags in our model. The recommended way is to add an [AllowHtml] attribute to the model property that may contain HTML and no validation happens for that property. Alternatively, it’s possible to add an [ValidateInput(false)] attribute to the controller action that accepts the model with HTML content, but this turns off validation for all model properties.

A potentially dangerous Request.Form value was detected from the client

Wait a minute! Didn’t we just explicitly say to allow those dangerous values? What’s happening?

It appears that a call to bindingContext.ValueProvider.GetValue() in the code above always validates the data, regardless any attributes. Digging into the ASP.NET MVC sources revealed that the DefaultModelBinder first checks  if request validation is required and then calls the bindingContext.UnvalidatedValueProvider.GetValue() method with a parameter that indicates if validation is required or not.

Unfortunately we can’t use any of the framework code because it’s sealed, private or whatever to protect ignorant devs from doing dangerous stuff, but  it’s not too difficult to create a working custom model binder that respects the AllowHtml and ValidateInput attributes:

public class MyModelBinder: IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // First check if request validation is required
        var shouldPerformRequestValidation = controllerContext.Controller.ValidateRequest && bindingContext.ModelMetadata.RequestValidationEnabled;

        // Get value
        var valueProviderResult = bindingContext.GetValueFromValueProvider(shouldPerformRequestValidation);
        if (valueProviderResult != null)
        {
            var theValue = valueProviderResult.AttemptedValue;

            // etc...
        }
    }
}

The other required piece is a way to retrieve an unvalidated value. In this example we use an extension method for the ModelBindingContext class:

public static ValueProviderResult GetValueFromValueProvider(this ModelBindingContext bindingContext, bool performRequestValidation)
{
    var unvalidatedValueProvider = bindingContext.ValueProvider as IUnvalidatedValueProvider;
    return (unvalidatedValueProvider != null)
               ? unvalidatedValueProvider.GetValue(bindingContext.ModelName, !performRequestValidation)
               : bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
}

NuGet package for MVC pager

It’s now possible to add the MVC pager to your own project with NuGet.

image

Or from Visual Studio with ‘Manage NuGet Packages…’ and then search for ‘MvcPaging’.

After installing, you can immediately use the Pager HtmlHelper in your views:

image

et voilà!

image

We also have include the IPagedList for your convenience:

image

For more info, check out the original blogpost that started it or the github project page.

Enjoy!

Keep your users informed with ASP.NET MVC

In almost every application, feedback from the application to the user via notification messages is required.

success

For some strange reason, we don’t have a standard way in .NET web applications for dealing with this kind of messages and I find myself hacking some half-baked solutions over and over again. Starting from today, this must end! In this post we’re going to find a solution for ASP.NET MVC 3.

One single way to display messages

This is the one and only requirement:

In every scenario I want to be able to call ShowMessage() from my controller action and the message appears on the screen.

ShowMessage extension method

To have a ShowMessage method in every controller action, we could implement it in a base controller class or create an extension method. Let’s try an extension method:

public static void ShowMessage(this Controller controller, MessageType messageType, string message, bool showAfterRedirect = false)
{
    var messageTypeKey = messageType.ToString();
    if (showAfterRedirect)
    {
        controller.TempData[messageTypeKey] = message;
    }
    else
    {
        controller.ViewData[messageTypeKey] = message;
    }
}

All this method does is take a message type enum (Success, Warning or Error) and a message and store it in either ViewData or TempData, depending on the flag ‘showAfterRedirect’. TempData is for Post-Redirect-Get situations.

We can now call ShowMessage(). With this solution it’s only possible to store one type of message per request, but that should be enough (even more than one message is already troublesome).

RenderMessages HTML Helper

Creating messages is one thing, but what about displaying them? It’s also easy. Enter the RenderMessages() HTML helper:

/// <summary>
/// Render all messages that have been set during execution of the controller action.
/// </summary>
/// <param name="htmlHelper"></param>
/// <returns></returns>
public static HtmlString RenderMessages(this HtmlHelper htmlHelper)
{
    var messages = String.Empty;
    foreach (var messageType in Enum.GetNames(typeof(MessageType)))
    {
        var message = htmlHelper.ViewContext.ViewData.ContainsKey(messageType)
                        ? htmlHelper.ViewContext.ViewData[messageType]
                        : htmlHelper.ViewContext.TempData.ContainsKey(messageType)
                            ? htmlHelper.ViewContext.TempData[messageType]
                            : null;
        if (message != null)
        {
            var messageBoxBuilder = new TagBuilder("div");
            messageBoxBuilder.AddCssClass(String.Format("messagebox {0}", messageType.ToLowerInvariant()));
            messageBoxBuilder.SetInnerText(message.ToString());
            messages += messageBoxBuilder.ToString();
        }
    }
    return MvcHtmlString.Create(messages);
}

It iterates through all possible message types, tries to find a message in either ViewData or TempData and creates a div with the message in it.

Put the helper in the Layout page, add some css classes (.messagebox, .succes, .warning and .error) and we’re all set.

What about AJAX?

Now this is getting tricky. In our AJAX actions, we can still call ShowMessage(), but how should the message be displayed if we only return some JSON data or a partial view? One solution would be to wrap all AJAX results in a special JSON view result with the notification messages and the original result embedded. Drawback is that we have quite some work to do on the client-side to handle all this.

HTTP headers to the rescue

I discovered a very nice solution on StackOverflow: use custom HTTP headers to store a message. With the help of a global action filter we can check if there are messages in the ViewData dictionary and when the request was an AJAX request, copy the messages to a custom HTTP header.

/// <summary>
/// If we're dealing with ajax requests, any message that is in the view data goes to
/// the http header.
/// </summary>
public class AjaxMessagesFilter : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            var viewData = filterContext.Controller.ViewData;
            var response = filterContext.HttpContext.Response;

            foreach (var messageType in Enum.GetNames(typeof(MessageType)))
            {
                var message = viewData.ContainsKey(messageType)
                                ? viewData[messageType]
                                : null;
                if (message != null) // We store only one message in the http header. First message that comes wins.
                {
                    response.AddHeader("X-Message-Type", messageType);
                    response.AddHeader("X-Message", message.ToString());
                    return;
                }
            }
        }
    }
}

x-message

Finally the message in the HTTP headers are displayed with a little bit of jQuery:

$(document).ready(function () {
    handleAjaxMessages();
});

function handleAjaxMessages() {
    $(document).ajaxSuccess(function (event, request) {
        checkAndHandleMessageFromHeader(request);
    }).ajaxError(function (event, request) {
        displayMessage(request.responseText, "error");
    });
}

function checkAndHandleMessageFromHeader(request) {
    var msg = request.getResponseHeader('X-Message');
    if (msg) {
        displayMessage(msg, request.getResponseHeader('X-Message-Type'));
    }
}

function displayMessage(message, messageType) {
    $("#messagewrapper").html('<div class="messagebox ' + messageType.toLowerCase() + '"></div>');
    $("#messagewrapper .messagebox").text(message);
}

Demo

For a closer look check out the live demo or download the VS2010 solution from GitHub.

MVC Pager update: now with AJAX support

The MVC pager now has support for AJAX scenario’s, thanks to Bart Lenaerts. This was my first GitHub pull request and everything went pretty smoothly.

Check out the demo application for an example. Visit the GitHub project page to download the solution.