Search code examples
javaspringspring-mvcthymeleaf

Displaying "Passwords don't match" custom annotation message


How to display this message from custom class type annotation in Spring as a warning message in HTML form? I am using Spring Boot MVC and Thymeleaf. Thymeleaf can handle field errors and global errors that are not associated with any specific fields in the form, but still exist. If this custom annotation finds that passwords don't match is this global error or field error? How can I show this message?

Custom annotation:

@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordMatchesValidator.class)
@Documented
public @interface PasswordMatches {

    String message() default "Passwords don't match";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

Validator class:

public class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, Object> {

    @Override
    public void initialize(PasswordMatches passwordMatches) {
    }

    @Override
    public boolean isValid(Object obj, ConstraintValidatorContext context) {
        UserDto userDto = (UserDto) obj;
        return userDto.getPassword().equals(userDto.getMatchingPassword());
    }
}

Post Controller

@PostMapping(value = "/register")
public ModelAndView processRegistration(ModelAndView modelAndView, @Valid @ModelAttribute ("userDto") UserDto userDto,
                                        BindingResult bindingResult, HttpServletRequest request, Errors errors) {
    // Lookup user in database by e-mail
    User userExists = userService.findByEmail(userDto.getEmail());

    System.out.println(userExists);

    if (userExists != null) {
        modelAndView.addObject("alreadyRegisteredMessage", "Oops!  There is already a user registered with the email provided.");
        modelAndView.setViewName("register");
        bindingResult.reject("email");
    }

    if (bindingResult.hasErrors()) {
        modelAndView.setViewName("register");
    } else { // new user so we create user and send confirmation e-mail

        User user = userService.createNewAccount(userDto);
        user.setEnabled(false);
        userService.saveUser(user);

        modelAndView.addObject("confirmationMessage", "A confirmation e-mail has been sent to " + userDto.getEmail());
        modelAndView.setViewName("registered");
    }
}

HTML form:

<div class="container">
  <div class="row">
    <div class="card col-6 mx-auto" style="width: 20rem; margin: 20px;">
      <div class="card-body">
        <h4 class="card-title">Registration form:</h4>
        <form th:action="@{register}" th:object="${userDto}"  th:method="post" method="post" action="register">
          <div class="form-group">
            <label th:for="name" for="Name">Name</label>
            <input type="text" class="form-control" th:field="*{name}" id="name" placeholder="Enter name">
            <p class="text-danger" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></p>
          </div>
          <div class="form-group">
            <label th:for="surname" for="Surname">Surname</label>
            <input type="text" class="form-control" th:field="*{surname}" id="surname" placeholder="Enter surname">
            <p class="text-danger" th:if="${#fields.hasErrors('surname')}" th:errors="*{surname}"></p>
          </div>
          <div class="form-group">
            <label th:for="username" for="Username">Username</label>
            <input type="text" class="form-control" th:field="*{username}" id="username" placeholder="Enter username">
            <p class="text-danger" th:if="${#fields.hasErrors('username')}" th:errors="*{username}"></p>
          </div>
          <div class="form-group">
            <label th:for="email" for="Email">Email address</label>
            <input type="email" class="form-control" th:field="*{email}" id="email" aria-describedby="emailHelp" placeholder="Enter email">
            <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
            <p class="text-danger"th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></p>
            <p class="text-danger" th:text="${alreadyRegisteredMessage}"></p>
          </div>
          <div class="form-group">
            <label th:for="password" for="Password">Password</label>
            <input type="password" class="form-control" th:field="*{password}" id="password" placeholder="Password">
            <p class="text-danger"th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></p>
          </div>
          <div class="form-group">
            <label th:for="matchingPassword" for="matchingPassword">Confirm Password</label>
            <input type="password" class="form-control" th:field="*{matchingPassword}" id="matchingPassword" placeholder="Password">
            <p class="text-danger"th:if="${#fields.hasErrors('matchingPassword')}" th:errors="*{matchingPassword}"></p>
          </div>
          <button type="submit" class="btn btn-primary">Submit</button>
        </form>
      </div>
    </div>
  </div>
</div>

Solution

  • The solution is to include global errors Thymeleaf syntax in HTML form. But for some reason single quote in this message: "Passwords don't match", doesn't get rendered. The answer to this problem is here.