How To Implement Validation With MediatR And FluentValidation

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi everyone my name is Milan and in today's video I'm going to show you how you can elegantly Implement validation in your clean architecture by using mediator's pipeline Behavior and the fluent validation Library when I say validation in this context I'm referring more to the input validation of the request objects that are being sent to your API and not business or data validations that I think are more appropriately done in the application layer by the end of this video you're going to learn how to use mediator's pipeline Behavior how to integrate validation using the fluent validation library and how to compose everything together inside of an asp.net web API so let's see what we have in the code we're going to start off from the I validation result interface which I previously prepared this interface represents the validation result which will be returned from our pipeline behaviors and I'm going to show you this in just a few minutes what we have in this interface are the static read-only field which represents the validation error and the errors array which is obviously going to represent our validation errors I already created the two implementations of this interface the first is the validation result the validation result inherits from the base result class and implements the I validation result interface and it contains a static method for creating a new validation result by specifying an array of errors we also have the generic validation result similarly it inherits from the generic result class and implements the I validation result interface and we are going to be using these two validation result classes inside of our pipeline behavior let's go to our application project and create a new folder which I will call behaviors inside of this folder I'm going to create our pipeline behavior I'm going to call it validation pipeline Behavior all right get rid of these using statements I'm going to make the class public and the validation pipeline Behavior needs two generic arguments one is the type of the request that is being sent through the pipeline and the other is the type of the response and with these two generic arguments in place we can implement the eye pipeline Behavior interface which is coming from the mediator Library I'm going to specify the T request and tier response and we also need to add a few generic constraints one is that the T request has to be an I request returning a type of T response and we also need one more generic constraint and that is that the T response must be of type result if you remember how we implemented our Command and query interfaces in the cqrs video you will know that we constrained our commands and queries to always have to return our result object to show you what I'm talking about I'm going to go over to the iquery interface you can see that here it implements the I request interface and Returns on the result of the response and also in our I command interface you can see that we have two versions of the I command interface one that just returns a result and the other one that returns a result of type T response back in our validation pipeline behavior let's go ahead and implement this interface all right so inside of this method we want to do a few things one is validate the request object if there are any errors return a validation result otherwise we want to return the result of the next delegate execution in order to implement this I'm going to introduce a new nougat package in the application layer the package that I need is called fluent validation I'm going to go ahead and install it how we are going to use it is inside of our validation pipeline Behavior I'm going to inject an innumerable of I validator which is coming from the fluent validation Library and I'm going to specify the T request as the generic argument I'm going to call the field validators and let's generate a Constructor all right now that we have the validators for our request we can go ahead and use them to implement our validation logic I'm going to get rid of these comments now and Implement our handle method the first thing that I want to do is skip the validation if there are no validators present so I'm going to say if not validators any then just await the request Handler otherwise if we have any validators then we need to somehow perform our validation logic wrap that result into a validation result which I just showed you and return that from our pipeline I'm going to create an array of error objects that will represent our validation errors I'm going to write a link you expression now which is going to generate our errors array so I'm going to say validators and select each of the validator and more specifically I want to select the result of the validate method execution I'm going to pass in our request object the validate method returns a validation result instance and this object contains a list of Errors so I'm going to chain a call to select many and I'm going to say validation result and select all of the errors that may be present on the validation result after this call we end up with a collection of validation failures and we are only interested in the not null validation failures so I'm going to filter them out using the wear method I'm going to say validation failure where the validation failure is not null all right now we need to project the validation failures into error object instances so I'm going to call Select failure and I'm going to create a new error for the error code I'm going to use the failure property name and for the error message I'm going to use the failure error message property I'm going to call distinct on this because I don't want duplicators to be returned and I'm going to call to array to materialize our errors array now that we have our errors array we have two scenarios either this array is empty and we can execute our request or it's not empty and we should return a validation result so let's say if errors any then we need to return a validation result and I'm going to tackle this in a moment otherwise we just execute our request Handler awesome so let's see how we would return a validation result for this I'm going to use some reflection magic to make it work I'm going to create a new method for creating our validation result it's going to be private static and it's going to be generic and I'm going to name the generic argument the result I will call it create validation result and it accepts the result generic argument the parameters are going to be the errors array and I also need a generic constraint that the T result must implement the result class now that we have our create validation result method defined I'm going to call it inside of the pipeline Behavior handle method I will say create validation result pass in the T response as the generic argument and pass in the errors array and that's it and now we can focus on how we can implement this method first I'm going to say if type of the result equals type of result in this case the generic result that we are returning is the base result class so here I want to return the non-generic validation result and I create a new instance by calling validation result with errors and passing the errors array now the compiler is going to complain here it's going to say that it can't implicitly convert the validation result to type the result what we can do is cast it using the as operator and I'm going to wrap this in Brackets and use the null forgiving operator because I'm certain that this will never fail and it is safe to do if our T result is actually a generic result object then we need to do a little bit more work I'm going to get the generic validation results type by calling type of on the validation result class I'm going to get the generic type definition and I need to make a new generic type that is going to match the generic arguments on the T result which is specified here how we are going to get the numeric arguments is calling type of T result we are going to access the generic type arguments property and select the first type which is also going to be the only generic type because our generic result class only has one generic argument now that we have created our appropriate generic type we need to call the get method on it and the method that we need is the width errors method I'm going to get it by calling the name of operator on the validation result class and get the appropriate method and now we have our method we can invoke it I'm going to pass in null as the object instance because our method is static and create a new nullable object array and pass in our errors as our argument and I also need a variable to capture the result of our method call so I'm going to say object validation result now the compiler is warning me that this could possibly be null so I'm going to add a few null forgiving operators because I'm certain that those will never produce another result and now that we have our validation result object we just need to return it and obviously I need to cast it to Key result so that I satisfied the E compiler all right now we have our validation pipeline Behavior implemented let's go take a look at the implementation again if there are no validators present we just execute the request Handler if we do have validators we execute the validation we select the validation errors we then project the validation errors into error instances and if there are any errors we return an appropriate validation result otherwise our validations have succeeded and we just execute our request Handler to make this actually work inside of our application we need to do a few more things first let's actually Implement a validator using fluent validation inside of the members folder I'm going to create a validator for the create member command I'm going to add a new class here which I will call create member command validators and this is a convention that I like to follow I always name my validators by the commands that they are validating and I append validator to the name of the class this is going to inherit from the abstract validator coming from fluent validation library and I'm going to specify the create member command as the generic argument and how you implement your validation is inside of the Constructor you add a few rules so I'm going to add a rule for the email and I'm going to say that it must not be empty I'm going to add a rule for the first name property I'm going to say first name is not empty and has a maximum length which will come from our first name value object if you recall which has a max length constant and I'm going to do the same for the last name property so it can be empty and it has a maximum length of last name map select all right so this is how we Implement our validator using the fluent validation Library this library is really amazing it contains a lot more validation rules that what I just showed you here it's also very extendable and there are ways to implement your own validators which you can use throughout your application now that we have our validator we just need to wire a few more things up with the application so I'm going to go over to program.cs first we need to register our validation pipeline behavior in the services so I'm going to say Builder services at scoped and I'm going to pass in the I pipeline Behavior as the interface and our validation pipeline Behavior as the implementation type and we also need to register our validators for this I'm going to add one more nougat package I'm going to type in fluent validation and the package that I need is the fluent validation dependency injection extensions this Library gives us access to an extension method on the iservice collection interface we need to call the add validators from assembly method pass in our application assembly so I'm going to say gatherly application assembly reference assembly and I'm also going to set include internal types to true all right this is everything we need to wire up our validation pipeline behavior and our validators from fluent validation and now we can see this in action before running the application I'm going to add a breakpoint inside of our validation pipeline Behavior now I'm going to start the application and head over to postman inside of Postman I already prepared a post request for registering a new member and if you recall from our create member command validator we said that the first name and the last name are not allowed to be empty so let's see what happens when we send such a request we hit the breakpoint inside of our validation pipeline Behavior you can see that the request is indeed the create member command and the first name and the last name are empty the validators list contains the only validator that we defined which is the create member command validator so we are going to hit our next step where we create our errors array after this executes you can see that we have two errors one is for the first name and it says that the first name must not be empty one is for the last name and it says that the last name must not be empty we go inside of our generic create validation result method the first check will return false because we return a generic result we execute our reflection code here to get the generic validation result and as you can see we are indeed returning a validation result of type system guide from our method we cast it here and we successfully return from our method and let's see what we get back in Postman surprisingly or not we don't get the validation errors that we just saw for the first and last name let's see why this is actually happening back in our application I'm going to head over to the members controller and I'm going to go over to the register member method and look at this line here if the result is a failure we are returning a bad request and specifying the error object as the result of the method but if you recall our validation result class which you can see here contains an array of errors that we actually want to return from this method so to support this I'm going to add a new method inside of our API controller Base Class this is going to be a protected method which will return an iaction result and I'm going to call it handle failure is going to accept the result object instance and I'm going to write a switch expression on the result I'm going to use pattern matching here and if the is Success property of our result instance is true then I want to throw a new invalid operation exception because I don't want this method to be called when the result is successful the next case that I want to check is if the result is an i validation result instance in this case I want to return a bad request but I'm going to add a helper method to convert our validation result to a problem details response the method is going to be called create problem details and it's going to return a new object that matches the problem detail specification now I can return a bad request and we call the create problem details methods to create our error response I'm also going to add a wildcard here which is just going to return a bad request all right now that we have our handle failure method in place let's use it inside of our members controller here instead of returning a bad request I'm going to say handle failure and just pass in the result instance so let's run our application now and see what we get for the response back in Postman I'm going to send the same request that we sent previously and let's see what response we get this time as you can see now we're getting a problem details response with a 400 status code and in the errors array we actually have our validation errors so we have the first name must not be empty and the last name must not be emptier so let's fix them let's for example fix the first name I'm going to pass in Milan as the first name and send the request again and as you can see the first name error is gone let's also try specifying a last name that is too long I'm going to copy this a few times that should be about 50 characters and this time we get a different error message it says that the length of the last name must be 50 characters or fewer and we entered 80 characters so both of our validations are actually working all right this was everything I prepared for you for today I showed you how we can Implement validation inside of our clean architecture by using mediators pipeline behaviors we also leveraged the fluent validation library to actually Implement our validation logic and I showed you how to compose everything together to function nicely inside of our web application I'm also going to comment on a similar approach that I've seen used quite often the other implementation that I've seen is throwing a validation exception here instead of returning a validation result and you catch this validation exception in your Global exception Handler you convert it into whatever response you think is appropriate and you return that from your API if you enjoyed this video consider leaving it a like for the YouTube algorithm so that we can reach more viewers and show them how to implement validation in their clean architecture subscribe to my channel so that you don't miss any of my future videos and until next time stay awesome
Info
Channel: Milan Jovanović
Views: 30,993
Rating: undefined out of 5
Keywords: cqrs, cqrs event sourcing, cqrs vs saga, cqrs saga, cqrs dotnet, cqrs .net, commands, queries, cqrs design pattern, cqrs pattern, cqrs validation, fluent validation, mediatr, mediatr behavior, mediatr behaviors, cqrs mediatr, cqrs fluent validation, cqrs web api, clean architecture, domain-driven design, ddd, cqrs rest api, cqrs restful api, cqrs command, cqrs query, cqrs command and query
Id: 85dxwd8HzEk
Channel Id: undefined
Length: 18min 33sec (1113 seconds)
Published: Tue Sep 27 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.