Development

Fluent Validation in Web API

Recently I was working on a Web API project that required some rather specific business logic both on the data transfer object (DTO) level, as well as an entity level. I had known about Jeremy Skinner’s Fluent Validation for some time but never had an opportunity to use it. I thought of a few other approaches that I could do, such as custom validators for data annotations, or maybe having the entities themselves be responsible for the business logic, or potentially using a series of validation services, but ultimately I felt as the complexity would grow, the risk of having unmanageable code would increase. I decided it was time to try using Fluent Validation, and was quite pleased with how things turned out, and as such wanted to share my experience. Like anything in software development, there are lots of of ways to solve a problem, some not necessarily better or worse, although certainly the type of project and what is susceptible to change can influence the direction you go in.

Enter Fluent Validation

Quoting Jeremy Skinner directly, “Fluent Validation is a small validation library for .NET that uses a fluent interface and lambda expressions for building validation rules”. Written by Jeremy Skinner and licensed under Apache 2, it offers a way to easily write validation rules for a given entity, evaluating to true or false, and then determining this in a streamlined manner throughout your code. The setup is also quite easy (simply a quick Install-Package FluentValidation from the package manager console or finding it in NuGet). I ended up creating a new project within my current solution dedicated specifically to validation, which was helpful from both a project reference perspective, as well as a way to group my validators together. I then added the validation classes within a hierarchy of folders so I could quickly find the ones I wanted.

Validators

Again borrowing from Jeremy Skinner’s site the following example is all you really need to validate something:

So in my case, I would have added the above validator to my Customer folder within the FluentValidation project.
Once established, you would then validate your entity as follows:

Simple, right? Even the rules (created above in the validator constructor) are quite easy to setup. Sometimes you will need custom validators as well. You can create that easily as follows:

So here we have a custom property validator that can now be used anywhere where you are performing validation. Jeremy mentions two approaches to this, either using a more traditional inline instantiation and setter, or by using an extension method which I actually prefer:

Validation Action Filter

I was extremely happy with how easy this turned out to be. I even used a service to localize the error messages so that hard-coded strings weren’t passed in and returned. My goal was to actually validate data transfer objects that come in, but the problem was I didn’t want to rely on the regular data annotations or even go through creating my own validators (although I certainly considered that as mentioned above). I liked the clean, singular area where all the validation took place, but I didn’t like that I needed to now call these explicitly to do the validation.

One thing that came to mind was that I already had an Action Filter setup for validating the models after model binding takes place. This was of course your simple ModelState.IsValid() in an action filer with a filter attribute to decorate on any actions where you need validation support. I felt it would be great to somehow reuse this filter not only for the “standard” validation, but also to dynamically determine which model is bound and then validate it accordingly (if there were any validators associated).

Doing this would require reflection to determine associated validators, as well as adding a new section to the aforementioned action filter for running said validators.

Since I was using Autofac, I went to the App_Start area of my Web API project, where the Autofac configuration took place, and explicitly added a new section in the startup for classes that inherit from AbstractValidator:

With this done, I could now “query” for a given class to see if it inherited from AbstractValidator and then run the associated validation. Great!
Now the fun part – since model validation is taking place on a given action (as per the above attribute) I need to update this to also include Fluent Validation.
First, i’ll share the whole code snippet in case you’re in a hurry, and then i’ll explain each section further below:

So the above code will run through and look at the given class and see if it has any validators associated to it, and if so, it will perform validation as per the above Fluent code.

Explanation

This is the basic validation that existed prior to my additions. Its job was to look at standard tags on the class for entity validation.

This next area of code will look at all of the parameters being passed into the action on the controller – if any of them are classes (for example DTOs or entities), then we’ll look at each class individually, retrieve any validators associated with them, and finally execute the validation logic.

Finally, as mentioned before, the above code will grab any validators we created and run the validation logic on them, returning an error if the entity was not in a valid state according to our provided logic. Although not shared here, you could certainly do something similar to what I did and use a localization service or resource files to retrieve language-specific error messages and inform the front end (unless, of course, the front end has their own dictionary of error codes and matching localized descriptions)

Conclusion

After putting this code together, I was able to easily validate my entities and DTOs before control got to my action filter. All of the validation for a given entity was in a nice hierarchical structure inside a project in my solution. This allowed both myself and the team to know right where to go for maintaining validation rules. It was easy for other team members to create a new class, specify their validation, and the rest just automatically worked.

I was thinking about some scenarios where this sort of route would not be preferable. Certainly there are many cases where attributes from the System.ComponentModel.DataAnnotations work just fine as is, and no need to go further. In addition, smaller projects may not even need this sort of validation or it can be handled elsewhere. I’ve worked on a few projects where there was a high amount of variability in the business logic; that is to say context played an important role in parsing one or more different types of rules. I think that in most of the cases I encountered, this direction of using an action filter + Fluent would still be my direction, albeit I’d end up with a rather large group of validation files – definitely something that Microsoft’s Code Analyzer would throw warnings on for the files being too long. Perhaps using another solution or just breaking apart the files even further would work. Another idea could be to leverage more services to group common business logic together and create it via a factory pattern into the validation as needed.

Add Comment...

Your email address will not be published.
Fields marked * are required

Leszek
1 year ago

String.Join(“,”, (validationResult.Errors.Select(err => err.ErrorMessage)))));

I think this will NOT return the same JSON/XML structure as ModelState ?
As you allow DataAnnotations to be used for validation it can lead to situation like depending on validation engine diff structure with errors will be returned to client/browser?

Some APIs do that. Sometimes you can get Errors: [“message”] and Errors: [ {“prop”: [“asd” , “asd2”]}] …

Do you know if it is possible to pass parameter within Attribute so diff validator could be selected for the same Input object?

[FValidate(“MiniRules”)]
public void Test1(myClass input)
{
}

[FValidate(“FullRules”)]
public void Test2(myClass input)
{
}

Leszek
1 year ago

Could you give a tip How to make it work with Ninject?

IValidator validator = (IValidator)actionContext.Request.GetDependencyScope().GetService(typeof(AbstractValidator).MakeGenericType(t));

About us

We are experts in creating dynamic solutions in web and creative disciplines that enable our clients to realize broad success. Our desire for innovative and usable solutions drive a passion of continual improvement that leads to a constant stream of more intuitive and streamlined results.

Visit

Search

Social