ASP.NET Core 2.2 & 3 REST API #21 — Validating requests with FluentValidation
Up Next: Extended Swagger Documentation
We’re going to see how we are going to validate incoming requests. First, we are going to use the standard way — without using any other package and secondly, we are going to explain why FluentValidation is a better approach.
In our CreateTag
endpoint, we receive a CreateTagRequest
. Let’s demonstrate some validation by doing 2 checks on the tag:
- Should not be empty
- Should only contain alphanumeric characters
If you were to do this in the controller, you’d just have these few lines of code, in your controller:
Obviously, this is not maintainable. For even the slightest change or addition you’d have to alter and review each and every controller method that has to do with tags? Does that mindset ring a bell?
Furthermore, .NET supports validation via attributes. For example you can add a [Email]
attribute to a contract and then just check if(!ModelState.IsValid)
, translating the model state error to an api response error.
But then, you’d have to:
- Store all the validation logic on our
Contracts
, which violates the Single Responsibility Principles
FluentValidation
That’s the clean way to do this.
Do not confuse contract/request validation with business logic validation
- Add
FluentValidation.AspNetCore
- Go to the
MvcInstaller
, on our.AddMvc()
chain.AddFluentValidation
- Fill in automatic registrations of everything contained in our assembly, like the previous auto mapper tutorial by:
(mvcConfiguration => mvcConfiguration.RegisterValidatorsFromAssemblyContaining<Startup>())
. This should find everything that contains theAbstractValidator
class.
Let’s create a new folder, Validators
. We’re going to follow the naming convention XRequestValidator
(where XRequest
is the contract name).
Create the CreateTagRequestValidator : AbstractValidator<CreateTagRequest>
and let’s get started. Things are pretty straightforward:
- Add a consturctor
- Add rules
This means, that this validation is automatically going to be applied here.
The next step would be to not have to map errors between the FluentValidation
and the Contract
layer.
First, we are going to create some new contracts:
ErrorModel
ErrorResponse
(returns a list ofErrorModel
)
Next step would be to create a filter:
Filters/
ValidationFilter : IAsyncActionFilter
. A filter is essentially middleware. Very briefly, a middleware is a tiny step on our request pipeline.
request -> step1 -> step2 -> ... -> controller -> ... -> step2 -> step1 -> response
.
For example:
- Authentication is one middleware
- Logging is one middleware (you can see printing in the dev console)
- Model State validation is one middleware
In fact, the ActionExecutionDelegate next
is the controller ‘step’ when you create your own middleware.
In the context of this filter, model state validations have already been applied. This means, that we can just intercept this invalid state and return the ErrorResponse
to the user.
Take a moment to read through the following code. It’s not complex and pretty self explanatory.
Last step would be to register the filter in our Mvc
pipeline.
.AddMvc(options => ... options.Filters.Add<ValidationFilter>(); ...);
That’s it
Tip: For custom validation logic use the .Must()
method.
Up Next: Extended Swagger Documentation
Code is available on Github and the instructional videos are located on YouTube.
Keep Coding