Fluent Validation in ASP.NET Core

Full source code available here.

I have written about Fluent Validation a couple of times. There is a new library available for .Net Core.

How to return to validation messages back to the caller is not immediately obvious. You might see the validators running, but the responses are missing! Here is how to solve that problem.

Step 1

Firstly add the package to your Web Api project.
Install-Package FluentValidation.AspNetCore.
If you are going to keep your validators in the Web Api project that is all you need to add.

But if you put the validators in a different project you need to add the FluentValidation package to that projecty
Install-Package FluentValidation.

Step 2

In startup.cs add –

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
        {
            options.Filters.Add(typeof(ValidateModelAttribute));
        })
        .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<PersonModelValidator>());
}

Note the line options.Filters.Add(typeof(ValidateModelAttribute)), we need to add that filter.

Step 3

Add the validation filter –

public class ValidateModelAttribute : ActionFilterAttribute
{	
    public override void OnActionExecuting(ActionExecutingContext context)	
    {	
        if (!context.ModelState.IsValid)	
        {	
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}
Step 4

Add a model and a validator.

public class PersonModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class PersonModelValidator : AbstractValidator<PersonModel>
{
    public PersonModelValidator()
    {
        RuleFor(p => p.FirstName).NotEmpty();
        RuleFor(p => p.LastName).Length(5);
    }
}

Full source code available here.

Fluent Validation with Web Api 2

Full source code here.

I wrote blog post in 2015 on using the Fluent Validation NuGet package for complex validation needs. In the post the validator checked that a create person request had at least one active primary phone and at least one active primary email. Using Fluent Validation this was easy.

The blog post used a simple console application, but I now realize that a lot of people are having difficulty using this in Web API, especially when they have to consume the response from Web Api and look for potential errors from the Fluent Validation package.

I see an approach put forward Matthew Jones, but I don’t like the response rewriting. If you are making a request to a Web Api for a Person, you are no longer getting a Person, you’re getting a ResponsePackage with Person as an object inside. It causes problems with testing – calling the action method directly from a test return a different object then when called via a web request.

public class ResponsePackage  
{
    public List Errors { get; set; }

    public object Result { get; set; } 
}

This requires quite a bit of extra work on the client side to get at the Result object. Testing is also complicated because a test calling the action method directly get a different response than a request being rewritten.
The rewriting also applies to all responses from the WebApi.

I propose a slightly different solution.

Step 1

Add the FluentValidation.WebApi NuGet package to the Web Api project and wire it up in the WebApiConfig class.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        //Fluent Validation
        config.Filters.Add(new ValidateModelStateFilter());
        FluentValidationModelValidatorProvider.Configure(config);
        
        //snip..    
    }
}
Step 2

Create a model and validator in a Models project.

[Validator(typeof(PersonCreateRequestModelValidator))] 
public class PersonCreateRequestModel
{
    public Guid PersonId { get; set; }
    public string Firstname { get; set; }
    public string Lastname { get; set; }
}
	
public class PersonCreateRequestModelValidator : AbstractValidator
{
    //Simple validator that checks for values in Firstname and Lastname
    public PersonCreateRequestModelValidator()
    {
        RuleFor(r => r.Firstname).NotEmpty();
        RuleFor(r => r.Lastname).NotEmpty();
    }
}

Step 3

Create the Web Api endpoint.

public IHttpActionResult Post([FromBody]PersonCreateRequestModel requestModel)
{
    // If we get this far we have a vaild model.
    // If we then saved the person to the database we would get an id for the person and return it to the caller.
    requestModel.PersonId = Guid.NewGuid();

    return Ok(requestModel.PersonId);
}
Step 4

Create a client to call the Web Api endpoints. In my example I use a console app, but you could use MVC or another Web Api project.

Create a HttpClient client to call the web service.

private HttpClient GetHttpClient()
{
    var httpClient = new HttpClient();
    httpClient.BaseAddress = new Uri(@"http://localhost:5802/api/");
    httpClient.DefaultRequestHeaders.Accept.Clear();
    httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    return httpClient;
}

Call the web service using the client and examine the response for success, HttpStatusCode.OK, or failure, any other status code.

private async <Task> PostToService(PersonCreateRequestModel model)
{
    var httpClient = GetHttpClient();
    string requestEndpoint = "person"; // full request will be http://localhost:5802/api/person
    HttpResponseMessage response = await httpClient.PostAsJsonAsync(requestEndpoint, model);

    WebApiResponse wrappedResponse;
    
    if (response.StatusCode == HttpStatusCode.OK)
    {
        var id = await response.Content.ReadAsAsync();
        wrappedResponse = new WebApiResponse(id, response.StatusCode);
    }
    else
    {
        var errors = await response.Content.ReadAsStringAsync();
        wrappedResponse = new WebApiResponse(errors, response.StatusCode, true);
    }
    return wrappedResponse;
}

Success or failure, I wrap the repsonse (in the client) without losing anything from the web service. WebApiResponse is a generic class and as such takes any type. The wrapped response is then returned to the caller to do with as they wish.

public class WebApiResponse
{
    public WebApiResponse(T apiResponse, HttpStatusCode httpStatusCode)
    {
        ApiResponse = apiResponse;
        HttpStatusCode = httpStatusCode;
    }

    public WebApiResponse(string error, HttpStatusCode httpStatusCode, bool isError) // isError is just a way to differentiate the two constructors. If <code>T</code> were a string this constructor would always be called. 
    {
        Error = error;
        HttpStatusCode = httpStatusCode;
    }
    public T ApiResponse { get; set; }
    public HttpStatusCode HttpStatusCode { get; set; }
    public string Error { get; set; }
}

The major benefits I see in the approach are that it is simple, very flexible, does not change anything coming back from the web service, testing is unaffected, and you are altering the Microsoft provided response pipeline.

Full source code here.

Complex model validation using Fluent Validation

FullĀ source code is available here.

A common problem is validating an object using a complicated set of rules.

I started using the Fluent Validation package some time back, it is commonly used with MVC and Web API applications but can be used with in any scenario that requires validation. It allows you to easily and quickly build flexible validation rules.

Of course it is possible to apply basic data annotations or build a custom validation attribute.

But out of the box Fluent Validation offers much more elaborate validators like, CreditCard, EmailAddress ExclusiveBetween and many more. It also has a Must validator that takes a predicate you define. This allows the creation of any complex rule.

For example, the validator can check that the person create request (shown below) has at least one active primary phone and at least one active primary email.

Fluent Validation can also be easily used in a console app, with data annotations you would have to jump through hoops to get validation working.

  public class PersonCreateRequestValidator : AbstractValidator<PersonCreateRequest>
    {
        public PersonCreateRequestValidator()
        {
            RuleFor(r => r.Firstname).NotEmpty();
            RuleFor(r => r.Lastname).NotEmpty();

            RuleFor(r => r.Addresses).NotNull();
            RuleFor(r => r.Addresses).NotEmpty();

            RuleFor(r => r.Addresses).Must(HaveAnActiveAndPrimary).WithMessage("One (and only one) address must be primary and active");
            RuleFor(r => r.Phones).Must(HaveAnActiveAndPrimary).WithMessage("One (and only one) phone must be primary and active");
        }

        private bool HaveAnActiveAndPrimary(IEnumerable<IActivePrimary> items)
        {
            if (items == null)
            {
                return false;
            }

            int activePrimary = items.Count(p => p.IsActive && p.IsPrimary);
            return (activePrimary == 1);
        }
    }

This a simple example of usage –

            var personCreateRequest = new PersonCreateRequest
            {
                PersonId = Guid.NewGuid(),
                Firstname = "Tom",
                Lastname = "Travers",
                Addresses = new List<Address>() { new Address { Street = "Main", IsActive = true, IsPrimary = false, }, new Address{Street = "Boylston", IsActive = true, IsPrimary = true} },
                Phones = new List<Phone>() { new Phone { PhoneNumber = "124",  IsActive = true, IsPrimary = false } }
            };

            var validator = new PersonCreateRequestValidator();
            ValidationResult result = validator.Validate(personCreateRequest);

Validating against multiple parts of the request

If you have the slightly more complicated scenario where you need either an active and primary phone or an active and primary address, add the following predicate –

        private bool AtLeastOneActiveAndPrimaryBetweenPhoneAndAddress(PersonCreateRequest p)
        {
            bool addressActiveAndPrimary = HaveAnActiveAndPrimary(p.Addresses);
            bool phoneActiveAndPrimary = HaveAnActiveAndPrimary(p.Phones);

            bool result = addressActiveAndPrimary || phoneActiveAndPrimary;

            return result;
        }

And call it like so –

        public PersonCreateRequestValidator()
        {
            RuleFor(r => r).Must(AtLeastOneActiveAndPrimaryBetweenPhoneAndAddress).WithName("request");

Of course you can’t now call Must(HaveAnActiveAndPrimary) on just the phones and addresses.

Models

  public class PersonCreateRequest
    {
        public Guid RequestId { get; set; }
        public Guid PersonId { get; set; }
        public string Firstname { get; set; }
        public string Lastname { get; set; }
        public List<Address> Addresses { get; set; }
        public List<Phone> Phones { get; set; }
    }

 

    public class Address : IActivePrimary
    {
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public bool IsPrimary { get; set; }
        public bool IsActive { get; set; }
    }

 

    public class Phone : IActivePrimary
    {
        public string PhoneNumber { get; set; }
        public bool IsPrimary { get; set; }
        public bool IsActive { get; set; }
    }

 

    public interface IActivePrimary
    {
        bool IsPrimary { get; set; }
        bool IsActive { get; set; }
    }

Full source code is available here.