NixMash Spring v0.1.6 adds Spring Security and a new registration form with validation. Below is the registration form displaying both global errors and individual property errors.
We’re using a UserDTO object and Hibernate validation for individual field issues like the First Name field above.
@NotEmpty
@Length(min=User.MIN_LENGTH_FIRST_NAME, max=User.MAX_LENGTH_FIRST_NAME)
private String firstname = "";
To display the message we include the Hibernate property layout in our messages.properties file.
Length.userDTO.firstname=First name must be between {2} and {1} characters
We’re using a custom EmailValidator to enforce full domain email syntax.
@Pattern(regexp=".+@.+\\..+", message="{ExtendedEmailValidator.email}") @Target( { METHOD, FIELD, ANNOTATION_TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = {}) @Documented public @interface ExtendedEmailValidator { String message() default "{ExtendedEmailValidator.email}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
UserCreateFormValidator
Perhaps the most interesting aspect of New User Validation is our UserCreateFormValidator class for catching issues like a pre-existing usernames or passwords not matching. The custom validator extends org.springframework.validation.Validator and is initialized in the Registration Controller.
@InitBinder("userDTO")
public void initBinder(WebDataBinder binder) {
binder.addValidators(userCreateFormValidator);
}
An @Override validate() method in UserCreateFormValidator makes it easy to test multiple scenarios, like testing for the existence of the username.
private void validateUsername(Errors errors, UserDTO form) {
if (userService.getUserByUsername(form.getUsername()) != null) {
errors.reject("user.exists", "User with this username already exists");
}
}
Thymeleaf
No post on form validation would be complete without a quick look at Thymeleaf on the client side.
The form includes a GlobalErrors area to catch multiple errors caught by the UserCreateFormValidator.
<div th:if="${#fields.hasGlobalErrors()}" class="has-error col-lg-offset-3 alert alert-dismissible alert-danger"> <button type="button" class="close" data-dismiss="alert">×</button> <label class="control-label" th:each="err : ${#fields.globalErrors()}" th:text="${err}">...</label> </div>
Each field includes a small bit of code to catch the possibility of a field error, including a label and the ability to change the field display.
<div class="form-group" th:classappend="${#fields.hasErrors('firstname')} ? 'has-error'"> <label for="firstname" class="col-lg-3 control-label" th:text="#{register.form.firstname}">[First Name]</label> <div class="col-lg-9"> <input type="text" th:field="*{firstname}" class="form-control"/> <label class="control-label" th:errors="*{firstname}">[error]</label> </div> </div>
All source code described in this post is available in NixMash Spring v0.1.6 on GitHub.