15.6. Studio: Spa User Validation¶
We’ll build on the User Signup studio from last class, adding in model validation.
15.6.1. Getting Started¶
Open up your spa-day
application and checkout the user-signup-pt2 branch.
15.6.2. Add Validation Annotations¶
Navigate to the User
model class. Add validation
annotations
to ensure these conditions are satisfied:
Username, password, and verify are required (they can’t be empty)
Username is between 5 and 15 characters
Email is optional
If provided, the email has the format of a valid email address.
The password is at least 6 characters long
15.6.3. Using the Model to Render the Form¶
In the UserController
, modify the add
method that displays the
form so that it passes in an empty User
object with:
model.addAttribute(new User());
This object will be accessible in the template, by name, as user
.
Tip
Now that you’re passing in an empty User
object, you may notice some redundant code
in your processAddUserForm
controller. Remove the model attribute additions
and update the user/add
template to make use of the model fields (eg. user.username
).
While you’re in the add.html
template remove the type="email"
designation from the email
input. The last studio had you add this type to provide some client-side validation on the email
field, but we shouldn’t consider that sufficient. Now that we know how to use model validation to
validate an email field, we’ll favor this technique over client-side validation. Even with client-side
validation (that is, in the browser), you should always validate data on
the server as well. You might want to provide constraints in addition to
or beyond what the browser does, and it’s also possible for a clever
(or, more often, malicious) user to bypass the browser’s validation. For this studio, we’ll remove the
input type to make it easier to test the server-side validation.
15.6.4. Validating Form Submission Data¶
Now that you have your form set up, go back to UserController
and
add validation on form submission by adding the @Valid
annotation to
the User
parameter that is bound, along with an additional
parameter: Errors errors
.
Warning
Remember, you must put this parameter directly
after the User
parameter in the method definition for it to work
properly.
Within the processAddUserForm
handler, check for errors configured by the
validation annotation using errors.hasErrors()
. If this returns
true
, return the user to the form.
15.6.4.1. Validating That Passwords Match¶
As we mentioned above, we are not able to use Spring’s validation
machinery to validate that the two password fields match given the setup
we have here. Checking errors.hasErrors()
will only tell us if there
are errors in other form data fields.
Last studio, we added some validation checks to make sure the password fields match.
Now we have two validation sections: one for the annotation-configured
validation (which checks errors.hasErrors()
), and one that checks
that the password fields match. Make sure they work in-sync with each
other to properly return to the form if any of the validation conditions
fail.
Tip
You can, in fact, validate that passwords match using annotations by taking a slightly more difficult approach than we’ve done here. We outline how to do so in the Bonus Mission section.
15.6.5. Test, Test, Test!¶
You made a lot of changes! Be sure to thoroughly test them to make sure everything works as expected.
15.6.6. Bonus Mission¶
Let’s set up our User
class so we can validate that the password
fields match using annotations.
Add a
private String verifyPassword
field toUser
, along with getters and setters.Add a new method,
private void checkPassword
, that comparespassword
andverifyPassword
. If neither isnull
and they don’t match, then setverifyPassword = null
.In both
setPassword
andsetVerifyPassword
, callcheckPassword
after setting the given field.Add
@NotNull
to theverifyPassword
field with the error message: “Passwords do not match”.Refactor the controller and
add.html
template to use the built-in, annotation-based validation instead of the manual password validation that we carried out previously. Be sure to update the verify field and label in the form to use the field on theUser
class, and to removeString verify
from theadd
method signature.
The result of these changes is that when the User
object is bound to
the request, both password
and verifyPassword
are set. Spring
does this by calling the setters on these fields. The setters call
checkPassword
, so when the second one is set (whichever that may
be), we’ll know that both password
and verifyPassword
are not
null
and we’ll compare them. If they don’t match, we manually
violate the @NotNull
validation on verifyPassword
by setting
that field to null
.