3.3 Model Validation
Never trust unvalidated user input! In this lesson, I'll show you how to validate data coming from the user. You'll also learn how to display validation errors.
1.Introduction1 lesson, 01:12
2.Getting Started2 lessons, 22:54
3.Request Handling5 lessons, 1:06:56
4.Displaying Data5 lessons, 1:15:23
5.Conclusion1 lesson, 00:39
3.3 Model Validation
As a general rule of thumb, you should never trust user input. Because after all, users are people and people make mistakes. You might but erroneous data in a field or you might completely forget to fill out a field. But then there's also the more sinister side of humanity, where a user is attacking your website. So, as you're writing your application, you need to thinking these things. You need to think about user input validation, as well as protecting your application from attacks. Now we're not going to spend a whole lot of time on security in this lesson. We are going to do one small thing that will make a very big difference in the security of our application. We are actually going to thwart cross-site request forgery attacks with just a couple of lines of code. But validation is also going to be very simple. Now we could write our own code for validating these fields. Like for example, we have the title and we have the content. And these are two things that we should say that are required. A user needs to provide both a title and the content. Well, we could write that code or we could rely upon MVC to do it for us. All we need to do is go to our message class, this is our model class. And we need to add some attributes to these properties, like for example, both of these are required. So we are going to add a required attribute. This resides within system.componentmodel.dataannotations. And there are a variety of attributes that we can use to validate user input. As far as the title and the content, this is really it. We're not testing to see if we have any special type of strings, like an email address or an all-numeric string. We just want to ensure that both of these are required. We can also go so far as to add an error message, so that if they don't provide information for that particular field, we can display an error message. But let's not do that yet. Let's just say that both of these things are required. And then we need to go to our controller. And we need to check to see if our model is valid. We do that, of course, with an If statement. But we have this property called Model State and this has a property called Is Valid. If the model state is valid, then we want to process the information. But actually what we want to do is check to see if it's not valid. Because that's a more simple thing to do, because if the model's state is not valid, then we basically want to return the view. And we want to pass the model back to the view, because that way, NVC and the view engine will know that, okay, this is not correct. We need to display the error messages that's in the model state. So if it's not valid, they will see the form again as well as any error messages that we need to display. but if it is valid then we want to once again, store the information and then redirect the user back to, in this case, the index. It's important that I point out that we are now passing an object to the view. This is the primary way of how we get data from the controller and into the view. You simply pass it. Now, notice that we aren't doing that up here in the other create method. That's because we aren't displaying any data. We are just showing the form. So, we don't pass anything to the view. But down here for a post request, we are taking the model that the user gave us and we are sending that back. So whatever the user supplied for the title and the content, that is still going to be there. So that they don't have to fill it back out. But it's also going to display error messages related to the model and the model state. Well now that our view is expecting an object, we need to tell it what type of object that it is. So, we go to our view and at the very top we add an @ followed by the model keyword with a lowercase m, and then we simply tell it what type of object that it is. In this case, it's a message object. So this is what we call a strongly typed view. This means that we can reference the data that is going to be displayed in this view using the API for our message object. So, if we wanted to, we could display the title that was supplied by using the @ sign, because we want to display something using a razor expression. We have to do that through code. So now we have this model property. And this is the message object that was passed to this view. So, if we wanted to display the title, we'd just say, Message, well, in this case MessageContent. But we would also do the same thing for MessageTitle. Then that will display that information here in the view. But now that we have a model defined for this view, we could use an HTML helper to not only generate some of these HTML elements, but also populate them with information. Like for example, for the message title field, we could use HTML. This is the HTML helper that has a variety of methods that we could use. One of those is called EditorFor. This creates an editor for whatever type of value that we tell it to. We're going to use a lambda expression and we're going to see that this is an editor for our title. So, we use the message title property. And then, this is going to create a text box because that is the default editor for a string value. So this is going to generate the HTML element as well as populate it with the value of the title. So we can get rid of that input element. There's also a different syntax that we can use. One that uses HTML. So we would do input and then we would have an asp-4 attribute, and then we tell it what property. So this is automatically going to assume that it is for our model. And in this case, lets do the message content. And then that will display an input element for our message content. So we can get rid of this other input element. And this is going to look exactly like it did before. So let's go ahead and run this. I'm going to use Control+F5 so that we can continue to edit our code without having to restart the application. Let's go to message/create. And this is a form that we had before, at least visually. If we look at the source code, we're going to see a few small differences. For example, now we have some CSS classes, we have text box and single line. We also have this data-val attribute set to true. But then we have this data-val required. And this has a message. It says, the message title field is required. And we see something similar for the content field. This is the error message that is going to display whenever a value is not supplied for one of these fields. And there reason why we are seeing it here is because we can use jQuery to display those messages, but we're not going to worry about that right now. But do know that we can customize these error messages. If you'll remember a few minutes ago, I said that that's something that we could do, and we will do that, because we want something a bit more user friendly. Well, let's test this out. Let's put in something for the content. This is some content and we will leave the title empty. Now, we aren't going to see any error messages because we haven't put them in the view. But we did see the form after submitting the form. So, we know that validation failed, otherwise we would have been sent to the index. So, if we go back to our view, we can add what we need in order to display the error messages. Now, we can do this in one of two ways. We can display them one at a time, for each individual field, or we can just display a summary of them. Let's start with displaying the individual messages. We'll use the HTML helper once again and then we have a method called the ValidationMessage and ValidationMessageFor. Now the ValidationMessageFor is what we want to use. This is for using it with a model. So we basically tell it what property that we want to display the message for. In this case, that's going to be message title. So let's change that to title. And so, if we go back to the browser and refresh, yes, we want to retry sending the post request. That way it will send the same data. And now we can see that error message. The message title field is required, but we can also change that message. Let's go back to our message class, and let's add our own error message. And we do this by passing a string value to the required attribute. And we can say please supply a title. That's a little bit better. And actually, we have to tell this that this is for the error message. So ErrorMessage equals Please supply a title. We can go ahead and do the same thing for the content. But instead of supply a title, let's say supply content. We can go back to the browser. Let's go ahead and refresh. Yes, we want to resend the post request, and we will see our updated error message. It says, Please supply a title. Now, because we did supply content, we don't see anything there. But if we clear out the content field and then submit it, well we didn't add the error message there, did we? So, we need to do the same type of thing that we did for the message title. Now, we could use the HTML helper to generate that HTML element. Or we can use the new tag helper syntax. So let's add a new span element here and then we need an attribute called the asp-validation-for. This is essentially the same thing as this ValidationMessageFor method, except that everything is in line as far as HTML is concerned. And then we just specify the property that we want to display the validation message for. Now you can't self close this. This has to be an opening tag followed by a closing tag. But now if we go back to the browser, and let's go ahead and refresh this, then we can submit the form and we are going to see both error messages, please supply a title and please supply content. Now, you don't have to worry about matching the different types of syntax. If you wanted to, you could use the EditorFor to display the message title and then you could use a tag helper to display the validation message. However, there's really no reason to do that. Just pick the syntax that you like and stick with it. I know some people that would opt for the HTML helper, whereas I know other people would opt for the tag helper. It doesn't matter which you choose. Now, instead of displaying each individual message, we can also display a summary that will group all of the messages together. And this is the approach that I typically take. There are two ways that we can display the summary. The first is using the HTML helper. It has this validation summary method that will display an unordered list of all of the error messages. Or we can use the new tag helper. And we need a DIV element here. It has an attribute called asp-validation-summary, and there are three possible values that we can assign to this attribute. All of which are values of this validation summary enu. If we look at those values, we have All, ModelOnly, and None. Well None is self-explanatory. It's not going to display anything. And All is also self-explanatory. It's going to display all of the error messages. But there could be some confusion between All and Model Only, because after all, aren't we displaying the model errors? And yes, we are. But we can also add errors to our model states. We can go back to the controller and we can say ModelStates.AddModelError, and then we could add our own error if we wanted to. So, we typically will always use the All value, because we want to display all of the error messages. And this too is going to display an unordered list, but it's going to put it inside of this div element. So if we go back to the browser, let's submit. And we should see both the validation summary at the top, and then we have the individual messages. Now, of course, in your applications, you typically won't use both of these approaches. You'll use either the summary or the individual messages. The only reason why I've done both here is for demonstrative purposes. Well, we've looked at the required validator, which is basically what this is. This is a class called RequiredAttributes, and if we look at the definition, we are going to see that this RequiredAttributes inherits from this ValidationAttribute. And we have a variety of attributes that are considered ValidationAttributes. And if we look at some of the other model classes, like inside of this account view models, we are going to see a variety of attributes used. Like here is the RequiredAttribute, and then there's this DisplayAttribute, which is something that we will talk about here in a few moments. I first of all want to point out a few other validators. One is this email address. This is going to ensure that the value assigned to the email property is a valid email address. Now it's not actually going to check if that email address exists, but it's going to assure that it meets the username at domain pattern. Then there's also a validator for the string length. And then there's one that will compare one property with another. So this is going to ensure that the password and the confirmed password are the same. If they aren't, then validation fails. And there's a variety of other ValidationAttributes. And they all reside within the system component model DataAnnotation name space. Well, this DisplayAttribute is for displaying something in the user interface. And we primarily use it for displaying a label. Like for example, inside of our create view, we just display the fields. We don't say what these fields are. But we can do that very easily by using a label and we can use the HTML helper here or the tag helper, I should say. And this is going to be for our message content. And by default, this is going to take the name of the property and use that as the value of the label. So, if we go to the browser. And let's do just a normal get request here. We see that the label is message content, and that's not very useful as far as the user is concerned. So this is where that DisplayAttribute comes into play. We can use Display, and we need to set the name equal to whatever value that we want to use for this label. So this is our content. So, we can just say Content. And let's go ahead and do the same thing for the title. So let's just copy that, and we will paste it. And of course, the name here can be Title. And then we need to go to our Creat View and we need to add that label. Now I am just going to copy what we have for the message content and paste that up there. We of course need to change this to Message Title. So, if we go back to the browser and refresh, we will now see title and content. And if we look at the HTML, let's scroll down, we will see that that for the label we have the for attribute. So, it is setting that automatically for us, because it knows that this label is for the message title field. So, whenever we click on these labels, it's going to put focus onto their respective fields. So we have a form now for creating a message, and it is validating user input. The only other thing that I want to do is protect it from cross site requests forgery attacks. And this is very, very simple. And you should do this for every post request. The first thing we want to do is inside of our form, we want to use the HTML helper, and call the antiForgeryToken method. This is going to generate a hidden input element and it's going to give it a unique value. And it's different for every Get request. So, let's refresh the page. Let's view the source and we will see a new input element. It has a name of __RequestVerificationToken. it is of course a hidden input element. And than we have the token value, and it's rather long and it's unique for every Get request. So, whenever we submit the form the controller is going to have to validate this anti forgery token. And we tell it to do that with just one line of code. We add an attribute called ValidateAntiForgeryToken. So, the controller is going to receive this token from the client. If that token is valid, then it's going to execute the create method. If it's not, then it's going to throw an exception. And so with just a few lines of code, we were able to validate the data coming from the user through the create form. We also protected it from cross site request forgery attacks. Well, in the next lesson we are going to start adding the ability to edit a message. We're not going to implement everything just yet, but we're going to kind of do what we did in this lesson. But there's just one small difference, and you will see what that is in the next lesson.