Nick Tullos
Nick Tullos

Reputation: 511

Fluent Validation’s ValidationException inside MVC

We are using the CQRS pattern and we have a problem with fluent validation's error handling. (CQRS pattern @ https://lostechies.com/jimmybogard/2015/05/05/cqrs-with-mediatr-and-automapper/ )

public class OtherSpecified : AbstractValidator<Command>
{
    public OtherSpecified(ApplicationDbContext context)
    {
        RuleFor(x => x.Other).NotNull();
    }
}

public class DepartmentSpecified : AbstractValidator<Command>
{
    public DepartmentSpecified(ApplicationDbContext context)
    {
        RuleFor(x => x.Department).NotNull();
    }
}

Now we inject our validation handler so we can run multiple abstract validator based on info from http://lostechies.com/jimmybogard/2014/09/09/tackling-cross-cutting-concerns-with-a-mediator-pipeline/. This works but because I can see the rules running in the foreach loop

public TResponse Handle(TRequest request)
{
    var context = new ValidationContext(request);
    var result = new ValidationResult();

    var list = _validators.ToList();
    foreach (var validator in list)
    {
        var results = validator.Validate(request);
        foreach (var validationFailure in results.Errors)
        {
            result.Errors.Add(validationFailure);   
            //temp testing code below
            if (results.Errors.Count > 0)
                throw new ValidationException(result.Errors);

        }
    }

    if (result.Errors != null &&
        result.Errors.Count > 0)
    {
        throw new ValidationException(result.Errors);
    } 
    return _inner.Handle(request);
}

The problem is that the fluent validation exception ( ValidationException ) is not handled. The rule errors bubbles up as

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: FluentValidation.ValidationException: Validation failed:

What I would expect is Fluent validation to handle the error and pass it back to the ajax request in json as validation errors

Upvotes: 1

Views: 6718

Answers (1)

Nick Tullos
Nick Tullos

Reputation: 511

I catch the ValidationExceptionwith an HandleErrorAttribute and return json to the ajax request.

public class ValidationExceptionHandlerErrorAttribute : HandleErrorAttribute
    {
        public override void OnException(ExceptionContext filterContext)
        {
            //only handle ValidationExceptions
            if ((filterContext.Exception as ValidationException) != null)
            {
                var result = new ContentResult();
                //Add errors to Model State so they are handled auto-magically
                foreach (var validationsfailures in (filterContext.Exception as ValidationException).Errors)
                {
                    filterContext.Controller.ViewData.ModelState.AddModelError(validationsfailures.PropertyName,validationsfailures.ErrorMessage);
                }
                //convert to json and return to ajax request
                string content = JsonConvert.SerializeObject(filterContext.Controller.ViewData.ModelState,
                    new JsonSerializerSettings
                    {
                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                    });
                result.Content = content;
                result.ContentType = "application/json";
                filterContext.HttpContext.Response.StatusCode = 400;
                filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
                filterContext.Result = result;
                filterContext.ExceptionHandled = true;
            }
        }
    }

and I wire up here

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        ..
        ..
        filters.Add(new ValidationExceptionHandlerErrorAttribute());
    }

Upvotes: 3

Related Questions