FluentValidation in ASP.NET WebAPI

Adam Connelly
ResDiary Product Team
7 min readJan 25, 2018

At ResDiary we’ve been using FluentValidation.net for several years now. Out of the box, WebAPI allows you to validate your objects using the standard MVC method of decorating your classes with attributes:

I’ve always had a few issues with this:

  • The validation is strongly coupled to the object being validated, so you can’t use a different validator to validate the same object depending on the situation.
  • It’s not easy to test just the validation in isolation.
  • It’s not very easy to add custom validation rules. Sure, you can create your own validation attributes, but it’s a bit of a hassle.

What FluentValidation.net allows you to do is create a validator for an object, and describe the validation rules using a fluent interface. For example, a validator for the CreateTableDto object above might look like:

Then, if you want to actually validate an object, you can use your validator like this:

The ValidationResult object that gets returned from the Validate() method also contains detailed information about exactly what has failed and why. For more details check out the FluentValidation.net docs.

But does it work?

One of the major reasons that I love this library is that I’m obsessed with TDD, and it’s really easy to write unit tests for FluentValidation validators:

ShouldHaveValidationErrorFor() and ShouldNotHaveValidationErrorFor() are extension methods provided by FluentValidation.net that make it easier to test your validators. The reason for casting null to a string in the first test is because there are two overloads of each method: one that takes an argument of the same type as the property you’re checking, and the second that takes an instance of the object being validated. The cast is just required to pick the correct overload.

Take a look at the testing page of the FluentValidation.net docs for more info.

Integrating with WebAPI

FluentValidation.net has built-in integration with WebAPI, but to be honest we don’t actually use it, so I can’t really comment on that part of it. I should point out here that the reason we don’t use the built-in integration isn’t because of any issues with it, but just because I didn’t realise it was possible at the time when we started using WebAPI.

Instead, we started off by just injecting whatever validators we needed into our controllers, and then manually calling the Validate() method:

So in that example, we’re checking that the request body has actually been included with the request, and returning a 400 if not. We then use our validator to validate the request details, and throw an ApiValidationException if the request isn’t valid.

What happens behind the scenes is that we have a custom action filter that catches the ApiValidationException, and then makes sure that we return a consistent response object from our API for all validation failures. The response object looks something like this:

Here’s the filter:

All this is doing is checking whether the action throws an ApiValidationException, and if so it sets the response to an object containing the details of the validation errors.

In line 15, there’s a call to _mapper.Map(). What’s going on here is that we separate our domain models from the DTOs that we use to transmit information from our API so that our API isn’t altered unintentionally when we make changes to the domain. We use AutoMapper to convert between the two. This isn’t strictly necessary, and isn’t really related to validation.

Just as a quick aside, you may have noticed that I just passed an IValidator<CreateTableDto> parameter into my API controller’s constructor. If you’re used to using some kind of IoC container you’ll know exactly what’s going on here, but for anyone who’s not sure, basically what’s happening is that previously we’ve configured our code so that if the controller needs an IValidator<CreateTableDto> as a dependency, the part of our code that creates the controllers automatically injects a CreateTableDtoValidator instance into it. I don’t really want to go into details of dependency injection and IoC here, because it’s a whole topic in itself, so you’ll just have to take my word for it!

Great! So where can we go from here?

So that gives us a basic mechanism for validating our API controller actions, but the issue is that we end up writing the same boilerplate code over and over again in our controllers, and we end up needing to inject steadily more validator objects as we need more objects validated. The second problem isn’t too big a deal as long as you split up your API controllers, but the first just seems like wasted effort, and is really just noise.

One option is to go down the route of integrating FluentValidation.net with WebAPI. That way you don’t need to write the validation code — you just need to check whether the model is valid or not, and take some sort of action:

This is obviously a lot better, but you still need to check whether the model is valid or not, and then take some kind of action. As part of researching this article, I came across a blog post from Matthew Jones that explains how you can get rid of the ModelState.IsValid check as well by integrating FluentValidation.net with WebAPI.

We don’t use this exact method at ResDiary, but not because there’s necessarily a problem with it. In all honesty I just didn’t consider doing it quite like he describes, and I’m not 100% sure if it was even an option at the time I was thinking about this.

Right. So if that’s not what we do, what approach do we take?

We use a couple of attributes to indicate what parameters should be validated:

  • Required — just does a simple check that the parameter is not null.
  • ValidateParameter — checks that the parameter is not null, and validates it using FluentValidation.net.

Additionally, the ValidateParameter attribute allows you to optionally indicate that the parameter can be null, if you want to do validation, but also want to make the object optional.

The way that this works is that we have a global action filter registered that checks whether any action parameters require validation, and if so it performs that validation and returns an appropriate response if the object isn’t valid:

Here’s a quick summary of what that’s doing:

  1. We iterate over each parameter in our API action.
  2. We then try to get the value of the parameter, and check whether the parameter is decorated with either of the attributes.
  3. If the parameter is null we return an error response if the parameter is marked as required (either through the Required attribute or because the AllowNull property of the ValidateParameter attribute is false).
  4. If the parameter isn’t null, and we want to validate it, we try to find a validator that can validate it, and if one exists we validate the object.
  5. If validation fails, we return an error response containing the validation failures.

So the ValidateParameter attribute allows us to easily write validators for objects, indicate what parameters should be validated, and allows us to return a consistent response from all of our API actions.

Problems, problems, problems…

Recently I came across another problem that couldn’t be handled by our existing validation. The issue is that to design a good RESTful API, you often want to split out some of the parameters from your main request object so that they can become part of the path. For example:

/api/Restaurant/{restaurantId}/Table

In the route above, the restaurantId comes from a path variable, so it’s not part of the object being validated. This means that when you’re validating your table, you don’t have the context of the restaurant it’s being added to.

What about if we want to specify the area of the restaurant the table is being added to, for example the bar, main restaurant, or private dining room? One way to do this would be to add an AreaId to the DTO:

If we want to make sure that the AreaId specified belongs to the restaurant we’re adding the table to, we’re out of luck, because the validator doesn’t have the full context of the request, and so doesn’t know the restaurantId.

One option is to just do that check in our controller action:

But that just seems a bit messy. Now, not only do we have the validation in two places, but we also get this validation message separate to all the other validation messages.

The option I’ve come up with so far is to create a new interface, INeedRequestContext, and to alter the validation filter to pass the WebAPI HttpActionContext to your validator so that you can grab whatever context information you need.

This means that you can write a validator that looks something like this:

What happens here is that the validation filter checks whether your validator implements INeedRequestContext, and if so it calls ProvideContext() with the information about the current request. This allows your validator to grab whatever additional information it needs to be able to validate the object properly.

You’ll notice I’ve defined two validation rules for AreaId. The first one just checks that it’s not null since it’s a required field, and the second checks that it actually exists, and is within the specified restaurant. That’s why the check in IsAreaInRestaurant() returns true if the areaId is null — because that’s already being handled by the NotNull() check.

The reason that I’ve split the two checks into two separate RuleFor() calls instead of chaining them is because I want to just use the default message for the NotNull() check, but I want to supply a custom message for our custom check.

The reason that I had to use a format string for the area not existing error message is to make sure that the _restaurantId parameter gets evaluated when the object is being validated rather than when the validation rules are defined. This means it will have the correct value rather than the default value of 0 every time.

In order to support this new functionality, I had to make a small change to the ValidateActionAttribute class to call the new ProvideContext() method:

Wrapping Up

So in summary, we can now write validation objects for our WebAPI request objects, unit test them, and supply context information so that they have all the information you need to perform your validation.

The one thing that I’m not mega keen on at the moment is that we aren’t accessing the action parameters in a strongly typed manner. What this means is that if we refactor our method and rename the parameters, the validation will fail. Off the top of my head, I can’t think of a decent way to solve this problem, but it’s something that we can maybe improve in the future.

--

--