Model Validation | Validation Attributes vs. FluentValidation | .NET 6

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
today's video is about model validation okay we're going to look at two approaches one is validation attributes and the other one is using an awesome and very loved library called fluent validation we're going to dive into this library and get familiar with some of its really cool features and by the end of this video you should have a very good understanding of how to integrate this library in your service hi everyone i'm here i do have a quick disclaimer if you're new to the channel so i am a microsoft employee and these are microsoft technologies but i'm not talking on behalf of microsoft these are my personal opinions so let's look at the project that we'll be using to get familiar with these concepts so what we're going to be working with is the boober breakfast application this is a fictional application it's basically a crud based api we're going to focus specifically on the create breakfast and we'll see how we can make sure that the details that are sent over from the client adhere to whatever rules we define okay so let's get familiar with the service so as we said this is a crowd-based system and we're going to focus specifically on the create breakfast so as you would expect the end point looks as following and in the body we pass properties that correspond to what we have over here right so you can pause the video and see the properties correspond to the ui we see on the left other than perhaps a list of images over here but let's imagine that this will be added in a future iteration okay moving on to the architecture of the project then you can see we have here two projects one which models the api definition and another one which contains the actual logic of our application okay so let's run the service and make sure that it works as we expect so dot net run and we need to tell it which project it's the boober breakfast project okay let's make our create breakfast requests we can create a breakfast and we can see that the response is similar to the request but it also has a server generated id and a last modified date time so where does model validation come into play well basically at the moment we can specify whatever we want over here so we can say for example that the end date is a year earlier than the start date and if we send this then it's simply stored as is right so we have no validation on the details that are sent right we can send here whatever we want and it'll simply be stored okay so let's start with the first approach which is using validation attributes on the request model so like we said over here we're modeling our api so inside here we have the create breakfast request which models the request that we just sent and over here we can use validation attributes like so so we can say for example that this has to have a minimum length of 3 and let's say a maximum length of 10. right then this rule will be applied to the name property okay now if we rerun it let's actually watch run it okay and now if we send the request again then remember the name must be between three and ten characters so let's send the request and we can see that it was still stored successfully okay so if you're surprised and you expected this not to work then let's look at our controller and try to understand why this didn't work okay so basically what happens we have our validation attributes the framework validates that the request adheres to whatever rules we have defined if the details aren't valid then it'll set the is valid property on the model state to false all right so this means over here we can say if the model state isn't valid then what we want to do is over here return a validation problem and pass it the model state so this over here is a dictionary with details about what went wrong right so if we make the request again then we can see we now get a descriptive error response which tells us that there was some validation problem and we have over here a list of errors with everything that went wrong over here we can see that the name property that we supplied caused the error and we even get here details about what went wrong okay now back in our controller luckily we don't have to have this in each one of our controllers because the framework gives this to us out of the box when we specify the api controller attribute so the api controller attribute gives us a couple of things out of the box one of them is automatic 400 responses which is basically the code over here is executed by the framework instead of us having to specify it and another one is automatic bindings so we no longer need this from body or all the other attributes over here like from route and so on so simply specifying api controller allows us to get rid of this and get rid of this as well and now if we make the request again then we get the same response like before without us manually checking if the model state is valid as you would expect out of the box we get other validation attributes that are similar to this stuff that are generic like email and so on but most of the time this probably won't be enough to actually cover your scenario okay so how do you create a custom validation attribute then as following so first of all let's imagine that we have some future attributes this is what we're going to be creating now which will be in charge of validating that the date time really is in the future right so let's imagine we have something like this both on our start date time and on the end date time so back over here let's create a new folder let's call it data annotations and inside here let's create our future attribute which inherits from yes the validation attribute yeah let's include what we need to include and over here let's add our attributes usage attribute and instead of i'll let's have this either be a property or a field or a parameter great then for our logic let's just make this nullable so basically what we have is our attribute which inherits from the validation attribute and we're overriding is valid if we return false from this method then the client will get an error message right so let's add here the namespace and make sure this behaves as we expect so making a great breakfast request and over here we can see we get two errors on the start and end daytime okay now if you want to customize this message over here then there are many ways to do it the easiest is just to override the error message over here but you probably won't want to override it in every place that you're using the attribute then back in the future attribute you have multiple options from formatting the error message or you can use another method called is valid which returns a validation result and receives also the validation context and then from here you can also return yes something like this right so we would return a validation result and then this error is what the client would seem let's just fix this to match the signature of the base class and of course you can also add here a constructor and pass the error message there are many options but since i wanted to dive into the fluent validation library then if this is the approach that you're taking then make sure to look at how you can customize the attribute for your scenario okay so if you're looking at this and you're loving the way it looks all you need to do is look at your request and you know exactly how it's going to be validated then you might be asking yourself why did jeremy skinner spend so much time creating fluent validation well there are multiple reasons first of all if you remember over here in the future attribute we receive an object this isn't strongly typed we can put this future attribute on anything even if it doesn't make sense right so we can do this over here as well and also as this grows and you have more validations on each one of the properties then this can become a huge mess so as you can see over here this is just two attributes but you can imagine what it would look like if we had more and what if i want to check for example that the end time is greater than the start time how do i do it over here right so this also becomes a bit more complicated another thing that is nice in fluent validation is that it's much easier to unit test also you get out of the box a lot of these attributes that you might have had to implement yourself and the error messages are very descriptive but also highly customizable so without further ado let's look at fluent validation for model validation so first of all let's get rid of all of this and this also let's get rid of these redundant using statements now next let's add the fluent validation package so that it add to the boomer breakfast project the package phone validation okay now that we have this let's go to our brooper breakfast project that's great here in new folder it's called validations and in here let's create our great breakfast request validator which needs to inherit from the abstract validator this comes from the fluent validation library and over here we need to give it the type that we want to validate in our case it's the create breakfast request great then over here we need to define a constructor so public great breakfast request validator and inside here we can define all our rules okay so how do you define a rule well yes let's get rid of all this and look at one example so we have our name and like before we want this to be between three and ten characters so now if we run the project again and we try making a request then this works why does it work well because we didn't wire anything together so the first thing we want to do is register the validator in our dependency injection ioc container so let's go ahead and do that so builder dot services add as scoped and i validator this comes again from the phone validation library so obviously create breakfast request let's just include what we need to include great now that we have this defined back in our controller then over here we can say okay from services give me the yes let's just wrap this then what we can do we can say over here validator dot validate and we can pass it our great breakfast request this method returns a validation result right so this comes from the fluent validation library and now we can check if this was successful so the way this looks is yes if it is not valid then we want to return the errors but because we want to return a nice error then what we want to do over here is return a validation problem like we did before and over here we want to pass it a model state dictionary which contains the details of the errors so for that let's create here a model state dictionary and let's add to it all the various errors so as you can see let me just wrap this so each one of the failures has a property name which is the property that caused failure and an error message which we get out of the box from this library now we can pass this model state dictionary to the validation proper method and will return a similar error message to what we had before so let's make sure we're still running great let's make the create breakfast request so as you can see we got the same error like before right so we get 400 and over here we have a very descriptive error message which tells us which property over here we get the values that we specified and we even get over here the number of characters that we actually entered okay pretty cool you're probably looking at this thing over here and already imagining me saying we can make this a bit better yes we can make this a bit better but for that we're going to need the phone validation.sp net core package so doubling add to the boober breakfast project the package punt validation dot sp net core great now that we have that we can go back to our program cs and we can make this better so instead of registering each one of our validators manually you can simply say add validators from assembly containing and over here we can give it one of our validators for example this or a solution that's a bit cleaner is to simply add over here some assembly marker and all this is is an empty interface which we'll use every time we want to pass this assembly so over here we can say i assembly marker and that's it and as you would expect what this does is its kansas assembly finds all those abstract validators and adds them for us and now running this this should work like before let's make the create breakfast request and we got the same error without us having to manually wire anything together the next thing we can do is we can actually get rid of this let's change it back to what it was before and let's get rid of all this as well and over here simply use their ads fluent validation method and what this line over here will do is it'll add some dependencies that fluent validation needs and it will plug into asp.net cores validation pipeline and it'll basically do the logic that we had in our controller for us so now if we send the same request again then without us having anything in our controller we get the same response like before now one thing that's important to note so when using automatic validation which means plugging into asp.net core's validation pipeline instead of manually validating like we did before in the controller so looking at the official documentation of fluent validation then you can see over here that they do not recommend using this approach anymore so you can pause the video take a look but for the purpose of this video we're going to leave it as is in the next video we'll see how we can use mediator and a mediator behavior to manually validate our objects but for the purpose of this video we're going to continue using automatic validation okay now that we're a bit more familiar with this package let's see how we can actually use it to enforce whatever rules that we want so back in our validator then like we said we have over here many built-in validators as you would expect there's also not no or not empty or regex validation or credit card and the list goes on there are many validators out of the box so if you are using phone validation look at the documentation and get familiar with the various built-in validators okay so for example what we had before but with the start date time then over here we can replace the attribute that we wrote before with simply doing yes something like this okay now if we run it then we can see that this fails and we get an error message on the start date time and if you need dependency injection then over here you can inject whatever you want so if you have for example a daytime provider then you can simply inject it over here and get the time from this object you know if you remember from the documentation before then using automatic validations we can't use anything asynchronous if you do then you'll throw a runtime exception what you would have to do is create a validator instance and call validate async all right so we have something like this and it should work the same thing like before great so this simple line replaces the entire attribute that we had before and more importantly we can have rules that take more than one property into account so over here we can say for example that the end date time must be greater than the start date time right as so now if we try making a request then you can see we get this descriptive error out of the box okay moving on to some other capabilities then we can also have your rule for each and over here we can define whatever we want to be true for each one of the items in the savory list for example as you can see this library is pretty intuitive and you'll probably be able to find out many of its capabilities without even reading the documentation right so as you would expect if you want to customize the error message then all you need to do over here is say with message and over here you can define your custom error message now if we make a request and let's say one of them is empty then you can see we get here the error message that we define and of course we can make this a bit more readable if we extract this to a method so we can do something like this and call it something like not be empty and of course we can also replace the function with the following so we can have this return bool and this be the savory item and then we can replace this thing with savory item and this is a bit more readable okay now i want to move on to features that are a bit more advanced and i haven't really seen covered in other tutorials so let's imagine we have some computation on the daytime that repeats itself multiple times and we would like to take that logic and extract it to our custom validation rule so we'd like to do something like the following so x dot start time and we would like to write over here after sunrise because again we're working on a system which handles breakfasts and you obviously can't have breakfast before sunrise right now i'm giving this example but basically what you have is some custom validator so how do you implement something like this well back over here in the validations folder let's create a new folder called custom validators and let's create here a new folder called daytime validators and the way this looks so we have a public static class daytime validators yes and what we'll have over here is it's following so public static i rule builder options and yes let's accept this and fix it a bit let's just wrap this so it's visible right so the way you read this is we have our after sunrise validator on whatever type that we have in our case it's the create breakfast request and inside this create breakfast request what we're validating is a daytime okay so now that we have this we can go back to our validator and add the namespace so now our method will be invoked when calling after sunrise and let's say that sunrise is always at 6am so we can have something like the following time only min value which is 12 a.m let's add to this six hours then over here we can say rule builder yes almost this will be time only from day time and then it'll give us only the time part of the daytime and that's it so just to recap we defined here our custom validator that has to have this weird signature where you basically put here the type that you want to validate and whatever name that you want then you have here the rule builder which you can use to actually define your custom rule what we did over here is we're basically saying that we want the time to be after 6 a.m right so now if we get rid of all the rest of the rules and we leave only this rule and do we make our create breakfast request again then since the time is after 6 a.m then we don't get an error but if we change this to 5 am then we get an error and as you can see this error message isn't very informative so let's go ahead and fix this so back in our custom validator let's just close this and this and over here let's give it a custom message so almost let's say time must be after sunrise and now if we make the same request again then we get our custom message but what if i need to access the property name then there are a few message placeholders so instead of time we can say over here property name must be after sunrise and then when phone validation populates the error message then it will put over here the property name so now if we run it again then instead of time we get here start datetime and there are a few other placeholders so as you would expect you also have the property value so you can say over here u provided property value right now if we run it again then we can see you provided and then the value that the user provided now as you can see the property value is the entire daytime but we actually only care about the time then what we can do is as following let's change this to a block body and over here instead of using this method let's use a different overload which gets the object root the date time and the validation context okay and then what we can do we can use the message formatter from the validation context to add our custom placeholders so for example yeah we can have here the sunrise and we can also have the provided time so let's extract this variable let's call this provided time and then other than the sunrise we can also have here the provided time to be the provided time and then over here in our message then we can simply use it so we can say sunrise and you provided provided time now if we make the request again then we can see we get here the property name the sunrise and the provided time okay now of course you can pass arguments to your custom validator so for example would make sense that you want to pass your your daytime provider so all we need to do over here is add our daytime provider and then we can get whatever values that we want from our provider so let's imagine that we have here midnight and we have similar logic that's based on our daytime provider of course this could be whatever we want so if we have a way to actually get the sunrise value then we can get it but don't forget what we said before about asynchronous methods okay another cool thing that you can do so let's imagine back in our breakfast request let's imagine this is modeled a bit differently so let's imagine that all these properties are inside a breakfast detailed object for example so what we might have is another folder that's common and inside here you might have the breakfast details and like we said it simply has all of these values so let's paste it over here and replace this entire thing with breakfast details like so right i'm back in our breakfast controller then over here let's access it via the breakfast details and lastly let's update our request then we have our breakfast details which has inside all of these properties let's just extend annotation so again just to recap we took some of the properties from the create breakfast request and we moved them to another object called the breakfast details then we updated the request as well to match the new structure okay now basically what we have is another object called the breakfast details you know it's not hard to imagine that we might have multiple requests all of which might have this breakfast details object so given that we have some breakfast details validator how do we reuse it in all the other validators right so let's look at how we would implement something like this so back in our validations let's create here a new file let's call it breakfast details validator and the type that we're validating is yes the breakfast details that's just include what we need to include so let's imagine that this is the logic that we have for our breakfast details and now we want to use these rules inside the create breakfast request validator right so the start date time is now part of the breakfast detail so let's come this out okay what we can do is we can say rule four the breakfast details set the validator to be a new breakfast detail validator right so i want to run this and make sure that this actually works as we expect so we're trying to create a breakfast we have a rule in the breakfast details validator that the name must not be empty so this should fail but first let's get rid of this trailing comma so we're making the request and as you would expect we get here a great error message which tells us that the name must not be empty okay another scenario that you might have so let's imagine that this create breakfast request is very big and you want to spread the rules across multiple files then what you can have is you can create over here a create breakfast request favorite items validator and the way this will look so we have your create breakfast request favorite items validator yes let's just include what we need to include okay so what we have over here we're validating the create breakfast request and we have here only a rule on the savory items so how do we include these rules inside another validator then we can go over here to our validator and we can simply say include and over here create a new create breakfast this long name validator now if we make a request and let's imagine that this is empty then we can see that it included the rules from the other validator okay now if you need anything more advanced than this then make sure to check out the documentation there are other customizations which i haven't covered and if you're using this library already or you really like what you saw today then make sure to give them a star i'm sure jeremy will appreciate the support okay so that's it for this video i really hope you have a better understanding of model validation and how you can use it in your application in the next video we're going back to our blooper dinner application and we'll see how we can create a custom mediator behavior which will invoke the appropriate validator before our commands or queries reach our handlers so if that sounds intriguing make sure to subscribe and i'll see you in the next one
Info
Channel: Amichai Mantinband
Views: 33,564
Rating: undefined out of 5
Keywords:
Id: -ix1hzWr2ws
Channel Id: undefined
Length: 27min 10sec (1630 seconds)
Published: Tue Aug 02 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.