The Importance of Server-Side Validation
What's going on guys? In this video we're going to take a look at data binding and validation using the Gin HTTP framework.
Server-side data validation is essential in order to follow three key rules to develop secure applications. Those three rules are: never trust user input, never trust user input, and again, never trust user input.
And if you want to learn data science with Python, check out DataCamp. There is a link in the description below this video. And you can also download the GoLand cheat sheet; there is a link in the description below this video as well. Remember to subscribe to the channel and let's get started.
Exploring Gin's Data Binding Functions
Okay, let's get started by understanding what are the options the Gin framework provides in order to bind the input. Here we have the post endpoint in our API that allow us to create new posts. Here we are passing the context, this struct gin.Context, and if we go there, here we're going to see that we are using this bindJSON function. And there are some other functions that we can use to bind the input. For example, we have bind, this one. We can use it to bind the form, the input form, or we can also use it to bind the query strings. We can bind the headers, the headers that we'll receive on the request. We can bind those by using this bindHeader function. We can bind JSON, that is the one that we are using. bindQuery to bind the query string, and we can bind the URI. And we can use some other functions, we can bind XML, YAML. So those are the functions that we have.
If we go within this function, this bindJSON function is a shortcut for mustBindWith. So if we go here, we're gonna see that if you get any errors, it's going to abort. And if you go here, we're gonna see that it's going to write the headers and it's going to return 400 status code and it's going to return plain text as the error. So if you want to have more flexibility to send the right headers with the content type that we want to send as the response, we should use shouldBindJSON. And we also have some other options. We have shouldBind for HTML forms and query strings. We can also use, we can also use shouldBindHeader, shouldBindJSON, shouldBindQuery, shouldBindURI, shouldBindXML, and shouldBindYAML. Okay, now let's move on.
To the video struct here. And here we can use struct tags. These are these struct tags where we can specify the different formats. We can specify JSON, we can also specify XML and a name associated to the tag that we are going to use. In that case, we can use form to specify the name of the field that includes the title in this case. We can also use, any date for example, and here we can say for example that this is gonna be, I don't know, an email just to keep an example. And we can also say that the binding is going to make this field required for example, required.
So these are some options. We're gonna see a few more now. So let's first create a new struct to assign an author to this video. So we're gonna create a new structure. It's going to be a Person and this person is gonna have a FirstName that's going to be string and we're going to use firstName as the attribute for the JSON format.
Sorry, it's firstName like this. okay. And sorry, LastName. And let's have the age of the person, just to be able to specify a range. So let's use int8 for this and we're going to use age here. And this person is gonna have an Email that's gonna be a string and the JSON field's gonna be email. And then here we can assign the author. Now that's going to be a Person and for JSON it's gonna be author like that. Okay.
Applying Built-in Validation Rules
Now let's add some tags for binding and validation of these two structs. The Title we want it to be between two and ten characters for example. So we can specify that by doing this: binding and we can say min=2 characters and max=10 characters for example, like that. And we want a Description with no more than twenty characters, so we can say max=20. And for the URL, we can make it required and we can validate the URL format by specifying that we expect a url like this. And for the Author, we want this field to be required. binding and we say here required like that. Okay.
Now let's add some tags to the Person struct. So I want the FirstName and the LastName to be required. And I want the Age, let's say that I want an age between 1 and 130. So we can say that we want the age to be greater than or equals to 1 and less than or equals to 130, like that. And for the Email here, let's use the validate tag and we can define that we expect a value here, so we set this is required and that we expect an email format. So if we don't get a valid email format on this field, it's going to throw an error. Okay.
Handling Validation Errors in the Controller
Now let's go back to the controller and let's make some changes there. So here we need to handle errors actually. So we're going to assign an error here and as usual we are going to check if error is not nil. In that case, we're going to return an error. So we want to make two changes here. Instead of returning a video, we are going to return an error in case we have any errors. And in this case if we don't get any error, we'll return nil.
And now let's go up to the server. Okay, and here I'm going to move this so I can handle the error in case we get any errors when saving the video. So here's the error. And I'm going to check if error... I'm going to return... I'm going to use the code, it should be, in this case it's gonna be Bad Request, status.BadRequest. This one. This is actually 400 as we can see here. And here we're going to use gin.H to return the body of the error. So we can say error and we're gonna use the Error() function from the error: err.Error() like that. And that is going to give us the description of the error. Okay. And yeah, I forgot to here, yeah, okay let's go back.
And if you don't get any errors... ctx.JSON... this is going to return a 200 code, http.StatusOK. And just for testing, we are going to add a message here. And this message is going to be "Video input is valid". Okay, now let's run this. Go run...
Testing Validation Rules in Postman
and let's go to Postman. Now we have a problem here with the validation of this age field because we are sending 0 as the value and this is expecting between 1 and 130. So let's say that we pass 200, we're gonna get the same error. And if we pass 20, yeah the input now is valid. Okay.
And let's say that we send an invalid email format here. And this is not going to validate the format because we are using validate but if we change this by binding, let's do that. And let's run this again. Let's go back to Postman. And if we run this now, we're gonna get the error that the email is not valid.
The title should be between 2 and 10 characters. So let's say that we send an empty title, this is going to return error. The key 'Video.Title' validation for 'Title' failed, basically the min tag because it's less than two characters. So if we add a title with more than 10 characters, it's going to fail. You can see field validation for 'Title' failed for the on the max tag because we exceeded this number of elements or this number of characters. So if we put 'title' that has five characters, we're going to be just fine, it's going to still fail on the email so we need to change that. And now we should be good to go. Yes, the input is valid.
So let's say that we put an invalid URL here. It's going to throw an error. Yeah, key 'Video.URL' field validation for 'URL' failed on the 'url' tag, because here we specified and here... here we specified the URL that we expect, a URL, and it's not a valid URL. That's why we get this error. Okay. And the same for the required. For example, the FirstName is required here. I'm going to fix the URL first. Okay, if we send an empty FirstName for example, this is going to throw an error. key 'Video.Author.FirstName' field validation for 'FirstName' failed on the 'required' tag. This is because we are not sending a value there. Okay, and now if we put the first name, the video input is not valid. Okay.
Implementing a Custom Validator
Now let's add a custom validation. Let's say that we want to add a custom validator that is going to check that the video is cool or not. So to add that custom validator first we need to specify it here. validate and we can call this is-cool. And if we go to the controller, so here we can do that on this New function. validate = validator.New() and this is going to import... let me see... this one. Just that. Okay. This is going to import this package. This is the package that Gin uses for validations. And I forgot to add a variable here. This would be var validate and it's going to be a reference to validator.Validate. Okay.
And here we need to associate this custom tag, this is-cool tag, with a custom validator. So this will be validate.RegisterValidation, this one. And the first parameter is the tag, that is is-cool, the one that we added here, this one. And the second one is going to be our validator. So I'm going to put nil now and I'm going to replace it after I create the custom validator.
So I'm going to create a new folder, I'm going to call it validators. And here I'm going to create a new file, let's call it validator.go. Okay, the package is gonna be validators, and the function, that's going to be ValidateCoolTitle and this is going to return, from the validator... FieldLevel like that. And it's going to return true or false, so it should be a boolean. Okay, and we need to return... and here what we're going to do is if the field contains the word 'cool', we're going to return true and in any other case we're going to return false. So this is going to be strings and we're going to, we're going to use the Contains function for strings, yeah. And we're going to use the Contains function and we're going to pass the field, fl.Field(). No, this is gonna give us the contents or the value of the field, and this would be a string. And we're gonna pass the word that we want to be included to return that the title is cool.
Let's go back to the controller. And now we can associate the tag with the custom validator that we just created. So it's gonna be validators.ValidateCoolTitle. So we associate the tag with the function that we just created that is actually this one. Okay.
And now what we have to do is after the binding and after handling the error we need to add the validation. So it's gonna be err = validate.Struct() and we pass the video. and here again we need to handle the error and we are going to return the error in case we get any errors on the validation.
Testing the Custom Validator & Summary
So let's run the server again and let's try this. Okay, let's go to Postman and we have the input that is valid. Okay, here we get an error: field validation for 'Title' failed on the 'is-cool' tag. Yeah because we are not sending a cool title, we are not including the word 'cool' here. So if we add 'Cool Title' and now we send the request, the input is valid. If we remove that, we're gonna get an error again. So, okay.
Now we have our custom validation running. So we added the binding with some fields required, we defined the range of values that we expect for an int for example in this case for the age of the person. We also specified the minimum and maximum for a title. We expect a maximum of characters for the description of the video. And we specified that we want the URL to be a valid URL, the same for the email. And we also specified that when we bind the video, the author is a required field. And the last one is this custom validator that checks that the title includes the word 'cool'.
Okay, that's pretty much all I have for today. Thank you for watching. Remember to subscribe to the channel and I see you guys in the next video. Take care, bye.