Christopher Steel

How to use FluentValidation to display validation errors and warnings in ASP.NET MVC

February 02, 2012

This post will provide a simple example of how to display multiple validation message types, like errors and messages, using FluentValidation. The ValidationSummary and TextBox messages will then be formatted differently, depending on the type, using an HTMLHelper.

The above screen shot is an example of a possible result using the method I describe in this post.

Validation is not just for Errors!

The following code is a simplistic implementation of the FluentValidation example for associating validation types, for example, errors and warnings, with validation results.

Take the following Model:

public class Customer
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Name { get; set; }
}

This can be validated using the FluentValidation AbstractValidator object, which uses a method in the inherited object to assign validation rules to the Customer class properties. It is possible to group the Rules into RuleSets and run them either at as part of the same process or separately.

public class CustomerValidator : AbstractValidator<Customer>
{
    //Customer validation rules which uses WithState to differentiate message type.
    public CustomerValidator()
    {
        RuleSet("NewCustomer", () =>
        {
            RuleFor(x => x.Name).NotNull().WithState(x => Enums.ValidationTypeEnum.error);
            RuleFor(x => x.Name).Length(1, 20).WithState(x => Enums.ValidationTypeEnum.error);
            RuleFor(x => x.Title).Length(0, 4).WithState(x => Enums.ValidationTypeEnum.error);
            //The WithMessage method is used to override the default message so it sounds more like a warning.
            RuleFor(x => x.Title).NotNull().WithState(x => Enums.ValidationTypeEnum.warning).WithMessage("'Title' is empty.");
        });
    }
}

The WithState method in the above Rules can be used to assign a validation message type – in this scenario the following Enum is being used:

public enum ValidationTypeEnum
{
    error = 1,
    warning = 2
}

In the Controller a List is populated in ViewData with the results of the Validate method. A specific RuleSet is passed in to validate against.

var validator = new CustomerValidator();
results = validator.Validate(customer, ruleSet: "NewCustomer");
ViewData["ValidationResults"] = results.Errors as List<ValidationFailure>;

Displaying Errors and Warning in the ValidationSummary HTMLHelper

This following uses the validation results collection created in the previous section to construct a new HTMLHelper that replaces the ValidationSummary. This will be used to display validation results differently depending on the associated Type, for example, Errors or Warnings.

The following HTMLHelper returns an MvcHtmlString to be used in the View. For simplicity the Enum is passed as a parameter.

public static MvcHtmlString ValidationSummaryFluent(this HtmlHelper html, Enums.ValidationTypeEnum type)

First populate a list with the results which were added to ViewData in the previous section.

var validationFailures = html.ViewData["ValidationResults"] as List<ValidationFailure>;

Then use the TagBuilder object to create the HTML String which will be returned by the helper. The following code uses the Enum parameter to assign a css class to a div tag from which we can derive different style formatting.

var tbDiv = new TagBuilder("div");
tbDiv.Attributes["class"] = string.Format("alert-message {0}",type);

Then loop through the list of validation results building up a group of messages to be output to the View.

//Restricting the loop to the parameter value
foreach (var validationFailure in validationFailures.Where(x => x.CustomState.ToString() == type.ToString()).Select(f => f.ErrorMessage).ToList())
{
    //Use validationFailure
}

Finally return the HTML String.

return MvcHtmlString.Create(tbDiv.ToString());

This can all be called from the View via the HTMLHelper:

<%= Html.ValidationSummaryFluent(Enums.ValidationTypeEnum.error)%>
<%= Html.ValidationSummaryFluent(Enums.ValidationTypeEnum.warning)%>

Displaying Errors and Warning against Form components

In this section a TextBox HTMLHelper is created which will be formatted differently depending on the validation result’s associated type, for example, errors or warnings. To keep the post simple I am not using EditorFor functionality which would enable better reuse of this helper.

First construct the new HTMLHelper function which accepts the model property as a parameter.

public static MvcHtmlString TextBoxForFluent<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)

Then get the collection of validation results created in the first section from the ViewData.

var validationFailures = html.ViewData["ValidationResults"] as List<ValidationFailure>;

Get the property name for use in criteria.

var nameOfTProperty = ((MemberExpression)expression.Body).Member.Name;

The following lambda expression returns the highest priority validation result for the relevant property.

var priorityFailure = validationFailures.Where(f => f.PropertyName == nameOfTProperty).OrderBy(f => f.CustomState).First();

The highest priority type is then assigned as the CSS class in a surrounding Div.

var tbDiv = new TagBuilder("div");
tbDiv.Attributes["class"] = string.Format("clearfix {0}", priorityFailure.CustomState);

The default TextBoxFor is then added to a StringBuilder which is later included in the InnerHtml property of the surrounding Div.

var sbInput = new StringBuilder();
sbInput.Append(html.TextBoxFor(expression).ToString());

Then to display the error message – loop through the validation results associated with the specific property and build a list of messages with the associated type formatting.

foreach (var validationFailure in validationFailures.Where(f => f.PropertyName == nameOfTProperty).ToList())
{
    //Use the validationFailure.ErrorMessage to build a list of messages
}

Finally return the HTML String.

return MvcHtmlString.Create(tbDiv.ToString());

This can all be called from the View via the HTMLHelper:

Html.TextBoxForFluent(model => model.Title)

Christopher Steel

Written by Christopher Steel, a freelance software development consultant who lives and works in St Albans, UK.