Building APIs with C# and ASP.NET Core

FluentValidation

Spencer Schneidenbach

Spencer Schneidenbach

Aviron Software, Microsoft MVP
Building APIs with C# and ASP.NET Core

Check out a free preview of the full Building APIs with C# and ASP.NET Core course

The "FluentValidation" Lesson is part of the full, Building APIs with C# and ASP.NET Core course featured in this preview video. Here's what you'd learn in this lesson:

Spencer introduces FluentValidation, an alternative to the validation patterns built into ASP.NET Core. FluentValidation allows developers to create separate validation classes from the request classes. It supports both sync and async validation, along with a clean API.

Preview
Close

Transcript from the "FluentValidation" Lesson

[00:00:00]
>> Spencer Schneidenbach: That's all pretty good and I'm gonna say that if you stop there and you're validating your API, I think that's totally valid.
>> Spencer Schneidenbach: There is a better way to validate that is not built into ASP.Net core, a library that I use a lot called Fluent Validation. I don't like the attributes as I don't like the validation attributes in general.

[00:00:23]
First of all, they lack a little bit of flexibility. If you need to add any kind of custom behavior to them, then you're forced to create a custom validator, which in and of itself, is fine, but the custom Validation Logic is a little cumbersome to write. It involves implementing something called ivalidatable object.

[00:00:39]
And on that target object. And I have to say, it's not pretty. I can't say that I love the syntax for actually trying to validate an object either. You can see that the arguments to try validate object are a little fairly convoluted. You can see that we get our employee request, we have to create a validation context for, as I mentioned, God knows what reason.

[00:01:06]
I do also believe that our request object because I believe in you to taskers, and I believe in one thing doing something really well. I wanna separate my request object from my validation object. And maybe the big killer of all of this is the fact that async validation.

[00:01:20]
So async where you're able to call async methods, is not built into asp.net core, which I believe is fairly large oversight in terms of the validation story. But luckily, we have an excellent library called fluent validation that we can use to handle all of this. First of all, we can create our separate validators.

[00:01:41]
We can separate those objects. We don't have to have required attributes. We can simply explicitly define them inside of a validator, which is what I often do. It supports both synchronous and async validation, which we'll see a few examples of later in the course. And it does provide a really clean validation API.

[00:01:59]
And it's truly fluent in that sense of the word. It almost reads like English. Let me show you first things first. We're gonna go to our project here, and we are going to go command P, and we are gonna focus on our NuGet view, boom. And I am going to.

[00:02:18]
>> Spencer Schneidenbach: Yes, ignore all. I'll stop with the pop ups VS code. We're gonna type in fluent validation, and we are going to install this into our project.
>> Spencer Schneidenbach: Awesome, looking good so far. And then we're gonna start by create employee request validator. We'll also install this other NuGet, so I'll hit the plus sign on that one, boom.

[00:02:43]
So it's fluent validation.dependency injection extensions, which we'll see a use of that in a bit. And so I'm gonna go ahead and copy and paste this, and we're gonna change up a little bit of the code. First of all, we are going to get rid of our required attributes.

[00:02:59]
We're not gonna need them here very shortly. And then I'm gonna scroll down, I'm gonna paste this in, and I'm gonna create a validator that first things first. It inherits from abstract validator. So I'm gonna hit command, I import fluent validation, and I'm gonna give it the generic argument of create employee request.

[00:03:17]
That's the object that we are validating. And I'm gonna say the rule for first name is that it should not be empty. That's pretty much the exact same thing. Empty in this case is defined as an empty string or a null string. So that's exactly what we want.

[00:03:36]
Lastly, we need to register builder.services.add validators from assembly containing. And that is when we installed the dependency injection extensions. We added a few tools that we can use to add these validators to our dependency injection pipeline, which we'll go ahead and do. So we're gonna go down here, builder.services.

[00:04:01]
We're going to using fluent validation, boom, add validators from assembly containing. And it's basically, you just give it a type of the thing that you want to have this method scan the assembly of. So we're just gonna say program, which is pretty typical. And now it's going to go through every type inside of program and figure out if it inherits from abstract validator, then it will add it to the dependency injection pipeline for use by our services.

[00:04:33]
So we can go down here and we can in our post endpoint actually get a copy of Ivalidator. So I'm gonna put this on this function. I'm gonna put these arguments on a separate line just to make it a little clearer. So we're gonna add in another argument to this lambda that we've created here.

[00:04:55]
And we are going to say Ivalidator, which if you look at the signature for Ivalidator. That is what the type is registered as inside of dependency injection. So we've got Ivalidator and we're going to say of create employee request. We're gonna say validate, pretty good so far. And then we can refactor our method to instead of doing all of this crazy stuff, we're going to just copy and paste a little bit of simpler code.

[00:05:31]
We have a.
>> Spencer Schneidenbach: Validator.validateasync, and we say, if validation results is valid, then we would just return the validation problem and we give it the validation results that we got back from our validator. One thing I wanna mention, you obviously see the squiggly lines here, and that's because we don't have this marked as an async method.

[00:05:55]
So we can mark it as an async task returning method by simply typing async. Right up here, that function modifier before our arguments list. And then you can see the compiler is very happy with that because now we have our async method, which is exactly what we want.

[00:06:10]
So let's test, let's run our tests and make sure that our validator method is working as we expect.
>> Spencer Schneidenbach: Okay, and we've got a little bit of a problem here, which is that our validation format coming back from validate our fluent validation endpoint doesn't have the exact same message as we would expect.

[00:06:37]
So we can fix our tests to ensure that that is. It's failing right here. So we will just go ahead and copy these lines. So the default error messages that you get back from fluent validation are of course going to have a slightly different language than what's in ASP.NET.

[00:06:54]
There's no worries there cuz we can simply update our validator. Our API is still young, we don't have to worry about that. So we'll go here, we will paste that in, go here, rerun our tests,
>> Spencer Schneidenbach: And you can see that they pass. So we've added dependency injection, we've added our validators to our dependency injection pipeline.

[00:07:17]
We've injected that into our method so that our method can use it, so that it can validate the object. And we've refactored the tests to make sure that these messages are what we expect. A couple other notes. I wanna go back to our create employee request validator here.

[00:07:34]
So, we say the rule for, and we give it an expression to select that property, and then we say, for that property we want it to not be empty. We can specify a couple of other things here. We can start by saying with message, and we can give it our own first name is required.

[00:07:58]
The error message that is being sent back to our client. The fluent part of fluent validation is kind of this chain method that, as I mentioned, kind of reads like English. These are typically referred to as fluent APIs, and they're just nice to use because they feel very natural, and they chain upon one another.

[00:08:17]
So first name is required. We would go back and we would see that our test would fail because we changed our error message. So we'll run our tests and you can see our test now fails. But if we go back here.
>> Spencer Schneidenbach: Say first name is required. If we wanted to specify a custom error message and you can see that now our endpoint happily says.

[00:08:39]
And happily sends back the correct message and that our test verifies that that's the error message that we're getting back, which is exactly what we want. So, you'll notice that in the method inside of the validator that we defined here. We defined a validator and you don't see really any async stuff.

[00:09:02]
And so why do we call async or validate async? Well, it's because of the nature of real world business programs often require that you might have to make some asynchronous call in order to validate that the object is as you expect. For instance, you might call out to a database to make sure that the object is in a specific state before you're able to allow allowed to update it.

[00:09:24]
So say, for example, that we wanted to verify that when we create an employee with a certain social security number. We wanna make sure that not only can the social security number not be empty, but it must be created and it must be unique within the system. We can actually refactor assuming that we had some kind of method on our repository to get employee by social security number.

[00:09:47]
We could actually create extend our validator to actually look inside of our repository if it's an asynchronous method which most repositories are gonna be asynchronous. I did the synchronous versions just for simplicity sake. And call this async task in order to make sure that the employee that we're creating is really unique by social security number.

[00:10:12]
And this is really important because this happens all the time in the real world. And this is, again, not something that you can do, at least in the built-in validation stuff at the ASP.NET Core. So anyways, this won't compile. I'm just providing it as a sample. So if you copy and paste it in, it's not gonna find the Git employee by social security number.

[00:10:34]
I do wanna call out one other thing, which is that you can use fluent validation to create some pretty complex validators, but in a kind of a very neat and organized way. You'll notice that right after I define my rule for social security number, I have this thing called a cascade rule.

[00:10:50]
And it basically says, if one validator fails, because for this property, we're actually validating that it's not empty and that it must be unique. We actually say that we want to stop execution of this if the first validator fails. The fluent validation's built-in behavior is to validate everything.

[00:11:09]
So this will allow it to short circuit and stop if the social security number is empty, then of course we don't need to check our database to see if it's unique because we don't have one specified. A couple of other cool features that it has. Is that because it's purpose built for validation and it is incredibly powerful, fluent validator does allow you to do some fairly unique stuff.

[00:11:33]
So we demonstrated that this social security number will stop after it determines that it's empty. But what if you wanna validate based on some kind of value that's coming in? Maybe you don't need to validate address to city, state zip, unless address one is not null. Well, you can say that for your rule that you can specify that if address one is not null, then we wanna go ahead and run these rules.

[00:12:03]
But if they haven't created the employee with an address, then we don't really care. So you can create all kinds of unique behaviors with fluent validation that make it really powerful to use. And it is my validator, this is the framework that I use for validation period in all of my ASP.NET Core API projects.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now