So we're going to do some server side form validation, and you might be asking yourself why?
I mean, look at this, make a reservation form.
If I try to fill it out and there's an error, it displays an error right there.
So we have client side validation and that's handled by our bootstrap client side validation.
And it works well.
It really does.
But you never want to trust user input.
Any time a user has an opportunity to enter something on a form, you can't make any assumptions.
So, for example, when I try to fill this form out, great, it works great.
If I put a first name in here that turns green.
But the problem is that is what we call client side validation, and that is implemented in almost every
case.
I actually can't think of another case.
Client side validation is handled using JavaScript and it is possible that a user might hit this form
with JavaScript turned off.
Now, they're not going to see a lot of things and like this drop down menu wouldn't work, but it's
possible they can get here.
And you have to assume the worst case scenario.
So what if someone hits this page and has JavaScript turned off in their browser, it's possible for
them to submit the form without all four required fields.
And another thing to bear in mind is that here, if I type in.
Yes, that's an email address.
That's not an email address that is seen as an email address.
Well, that's actually not a valid email address.
There is no such thing on the clear public Internet as an address in this format.
There might be on an internal network, but there's not on the outside world.
And our Web page is going to be served to the outside world.
So we definitely want to implement clients are server side form validation and we're going to do it
on this form.
So in order to do that, we need to make a few changes to our code, a few additions to our code, really.
So the very first thing I'm going to do is I want to create a new internal package.
So over in the internal folder, I'm going to create a new directory.
So new directory, which I will call forms.
So this is an internal package.
Inside of that, I'm going to, first of all, create three files, create one called Errors Dutko or
two files for now.
And I'm not going to to get you.
And then I'll create another one called Form Dongo.
So I've created these files.
And if I look at the list of the errors one first.
So what I'm going to do in the Errors package, which is right here, are the errors file in the forms
package is I'm going to create a type and I'm going to create a type.
You know what a type is.
We've done it many times.
We usually use a struct.
But in this case, I'm going to create a type with the name of errors.
So that's my name for this type.
And it's going to be a type of map, string, string.
So I've declared a package level variable that's not exported called errors.
And it's going to hold a map with an index of string and area value in that map will also be a string.
Now I'm going to create a new function and I'm going to call a function called ADD and this will be
used so func and I'll call it E errors.
So I'll tie it to that errors variable that we have.
So it has a receiver for that.
So now it has access to anything in that errors.
And I'm going to call this at and it's only going to add an error.
It gives us a means to say, hey, there's an error in our data that we receive from the form.
Please add this to the errors so we can do that pretty easily and we can do it by saying, well, what
field is the error in now?
A field?
Let's just bring up that we hide this and bring up under our templates that make reservation page.
So here we have four fields.
We have one called First Name with an idea.
First name.
It's called first name.
We have first name, last name, email and phone.
Perfect.
So that's what I mean by field.
So it's going to be a field and there's going to be a message associated with the error, which can
be whatever we want.
And these are both going to be strings and this doesn't return anything.
This is just a function that adds something.
So to our variable e from our receiver, we want to in the index field, which doesn't exist yet, but
don't worry about that.
We want to append E field and a message.
So when you look at it, I have a mistake up here.
There should be a slice of strings, not a string.
So our errors is a map of string with a contents being a slice of strings.
So we are appending our error to that particular slice.
And what we're putting in there is an error for a given field like first name and some kind of message.
Maybe its first name must be at least three characters or something like that.
It doesn't matter.
That's all that that function does.
And we're going to create one more function here.
OK, func also with the receiver on errors and it's going to be called GET.
And this is where we can get information.
So it takes one parameter field string and it returns a string.
And what we're going to do here is create an error string, just a variable, and that's going to be
assigned the value of whatever we find in our map with the index of the.
And if the length of X is equal to zero, then we want to do something.
What do we want to do?
We want to return nothing.
So there's no error for that.
This is all this is checking return an empty string.
But if it doesn't pass that I got a mistake here somewhere.
Don't there there are, but if it doesn't get returned at this point, then what do we do?
We return, yes, our error, string zero, the first index on the airstrip.
So this is a means of seeing whether or not a given field has an air.
And that's all that this does.
So let's give these things some comments, get what is get to it implements and get method.
It returns or retrieves the first error message returns the first error message.
And this will become clearer when we actually use these functions.
But this is not that common, complicated and add well, it adds an error message for a given field
and an error message for a given form field.
So that's all those functions do.
All right.
So we've got those functions created now over in our forms, package forms, page in our forms package.
This forms Dargo.
We need to put some things in here as well.
The first thing we need to do is to define a type.
This is what we're going to use to hold our form, all of the information associated with our form,
either when it's rendered for the very first time and the struct is actually empty or after the form
is submitted, when there may be one or more errors in there.
And we'll just call this form and it's going to be a struct and it will hold your URL values, which
comes from the net neutrality package.
OK, so it holds some values from the form and it holds errors, which is of type errors which we just
created.
OK, so that's an R struct is our type is and that's going to hold all of our form information.
And we also need to put a method here, a function here called New to create a new form.
And it's going to have a parameter of data which consists of Eurail values, which is built into the
gold standard library, and it returns a type of a pointer to a form.
So we're going to create a new form with a pointer to it.
And because it's a pointer, we can access it from anywhere using that pointer.
And no, we're pointing at the right form.
So all we have to do here is return something, returning a form, and since it's new, it's going to
be empty.
So we will say return a pointer to a form type, which we just defined up here.
And in that we're going to have data which is, you know, nothing right now that's just from this parameter.
So our data right there and it's going to have an errors type, which consists of a map of string and
a slice of strings.
No less so now we have a new function.
So what does that do?
Well, let's give it a comment so we won't forget new initializes the form structure.
So when it's initialized, it doesn't have any values in here at all except for the data that we passed
it.
And that was those would be corresponding to the inputs on our form field.
And we may as well give form our type a comment as well.
You're supposed to say, let's do that.
So it is formed.
Well, it creates a custom form struct and it embeds a neutral values object.
That's enough.
OK, so now we have our new function.
We have our type that is going to hold our form values.
And under airs we have a function to add an error and a function to get an error.
OK, so there's an error here somewhere.
Map, string, string.
Oh yeah.
I can't just put string like that because I'm declaring it to be empty, so I have to put the pretty
curly brackets there.
All right.
So this is great.
And what we're going to do in the forms go file is we're actually going to define certain checks to
see if the form data we received is valid.
So let's do one right now.
We're not using it yet, but let's do it.
And that's one of the things you definitely want to have when you're checking data server side, you're
validating data that the users have entered into a form and you want to make sure that the data is actually
same before you do anything.
Well, the very first thing I would have is let's make sure and we'll create a function with a receiver
of type form, pointer to form and we'll call it has a required field.
So you need to say, well, what actually is included?
What is the field that's required?
So that's going to be named field and that's a string.
So for first name on our form over wherever it is.
This page, for example, if this is a required field, well, the field I'm going to pass, it is the
name of the field in the form, which is ID.
So let's go back here.
So it's going to take a required parameter, a field that names that parameter that is required and
we're going to check to see if it's in there.
So to do that, we need to have access to the request object, what the browser sends to us.
And that is, as you know from our handlers, always a pointer to type HTTP request and it's going to
return lool.
So when I call this function, I want to say, does the form submitted by the user have a field named
first name, for example?
If it does, I'll return.
True.
If it doesn't, it returns false.
So checking it is really easy.
I can just do it like we did in our handlers.
When we there are kind of stub form handlers, I can create a variable which I'll call X and I'll just
check the request form.
Not yet.
And I'm going to get the field and we just look to see if that field has any data.
If X is equal to nothing, then there's an error return false.
There's not actually an error because it's OK to have an empty form field.
But in our case, we want to make sure this is a required field otherwise return.
True.
And that's all there is to it.
So we've created a custom function, a validator, and we'll give it its comment has and will describe
it as saying checks if form field is in post and not empty.
And that's a requirement for has.
That's all that it does.
So I've created these things.
How do I actually use them?
Well, we need to, first of all, set up a handler.
So let's go over to our roots and find make reservation and duplicate this.
And we'll call this one using my naming convention, because this is a post post reservation, which
doesn't exist yet.
But that's no problem.
We have to change this type to post as well.
So I've got the route set up now.
I have to go create the handler.
So I go over to my handlers file.
I find the reservation part right here.
I'm going to copy this.
And we have to make a change to this one in a moment and we'll paste it and we'll just leave this empty
right now.
And we'll change its name to post reservation and will change its comment to post reservation handle's.
The posting of a reservation form.
OK, now there's one more thing I need to do here, because I want to be able to check the form after
it's submitted, but I need to display the form before it's submitted.
So I need to add something to template data.
And I'm going to do that by going over to my templates day to go file and I'm going to add a column
here or add a member here and I'm going to call it form and it's going to be type of a pointer to form
start form.
OK, so I've added that did the import for me, so it's important properly.
Now there's a new member that I have on my template data, something that's sent to every page.
So whether a page has a form or not, I know that the form object is going to be available to me.
So let's go back to our handlers.
And now when I'm rendering this this reservation right here, I need to give it a form object.
And I can do that because it's part of the template, too.
So it's form and it's going to be equal to forms.
Dot knew that function was created just a little while ago.
I'm going to pass it.
No values at all.
So the very first time that form is rendered on this page, I have access to that forms object.
I'm not doing anything with it yet.
We will probably in the next lecture, but now I know I have access to it.
So that means it's available the first time it's displayed.
And then when I if somebody submits the form and they come back and there are errors, I'm going to
use this function.
The Horsfield, for example, the house function, for example, to say, aha, you needed to have a
first name, but you don't have a first name, therefore render the form, but this time display the
error.
And the great thing about this is I can display the error, but I can also display all of the other
information that the user entered will be able to do that eventually.
That's correct.
So I'm not going to just bounce them back to have them fill out a long form, had them submit it only
to discover after the fact that there's an error, take them back to the page with an error message
and have lost the rest of that information.
There's nothing more frustrating than that.
When you're working on a website and you fill out a form and you get an error message and you've lost
everything you've entered.
Well, we don't want that to happen to our users because we don't want to lose any potential reservations.
So we're going to make sure we display the error, give them some indication as to what the error is
and where it needs to be fixed and save all that other information.
And we'll do that in the not too distant future.
Right now, let's just make sure that everything works.
So I'm going to start my application, which I had running.
I'll compile it.
Go run command Web Stargirl.
It compiled and I should be able to go back to this form and reload the page and there it is and it
works just beautifully.
Now, of course, what one more thing I'll do before we call it a day for this lecture is I need to
turn off this custom of a client side validation.
So let me go back, find that page and turn it off temporarily.
And we can turn it off just by saying get rid of this class needs validation.
So I'll leave class in there and we'll have to remember to turn it back on.
But now when I come back and reload this, this should take me to a level that I actually have an action
in there.
Yeah.
Make reservationist the correct path for the roots.
I should be able to submit this and get an empty screen because our handler that handles it under route's,
wherever they're there, their post reservation doesn't do anything.
It's an empty it's an empty function right now.
So it just it should give me a white screen and it gives me a bad request.
Why?
Because I forgot on my make reservation page to put in my hidden field input type equals hidden name
equals SRF token value equals and in quotes see R.F. Token pulling that out of my template data that
passed to every page.
So it should be able to go back to that page, reload this form.
There should be a hidden field in there.
Now there it is.
Good.
OK, now I can submit an empty form and get a blank page and it does.
So I'm posting, I have created an empty form element.
Let's just go through this again.
We'll start right from the roots.
So somebody comes to the make reservation page and that tells them to go, I'm going to this page,
going to make Dasch reservation, therefore go to the handler called reservation in my handler's package
and reservation says, OK, just render that template, make reservation page tampoe and include an
empty form.
And that's exactly what it does.
What is forms new do?
Well, let's look at that.
All that does it say it's going to have a value of data.
You are values.
So that's some values taken from a form.
And because we're displaying this for the first time, this error, this data package is going to be
and it's going to be empty.
It's going to be nil.
And that's what I'm passing it in.
My handler's right here.
I'm passing it a value of nil.
As for the map, that's absolutely empty.
There's nothing in there right now in terms of errors at least.
So we're good to go now in the next lecture will actually try testing some of our one validator required
fields and we'll pick that up in the next election.