Search code examples
javaspringspring-bootspring-mvcthymeleaf

Spring Boot custom validator doesn't return to Controller


I am developing basic reset password flow. This is my DTO:

@Getter
@Setter
@FieldsValueMatch(field = "password", fieldMatch = "confirmPassword", message = "Passwords do not match")
public class PasswordResetForm {
    @Size(min = 8, message = "Password needs to have at least 8 characters")
    private String password;
    private String confirmPassword;
}

There is FieldsValueMatch annotation:

@Constraint(validatedBy = FieldsValueMatchValidator.class)
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldsValueMatch {
    String message() default "Fields values don't match!";

    String field();

    String fieldMatch();

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

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

And validator:

public class FieldsValueMatchValidator implements ConstraintValidator<FieldsValueMatch, Object> {

    private String field;
    private String fieldMatch;

    @Override
    public void initialize(FieldsValueMatch constraintAnnotation) {
        this.field = constraintAnnotation.field();
        this.fieldMatch = constraintAnnotation.fieldMatch();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        Object fieldValue = new BeanWrapperImpl(value).getPropertyValue(field);
        Object fieldMatchValue = new BeanWrapperImpl(value).getPropertyValue(fieldMatch);

        if (fieldValue != null) {
            return fieldValue.equals(fieldMatchValue);
        } else {
            return fieldMatchValue == null;
        }
    }
}

This is my controller:

@Controller
public class ResetPasswordController {

    @ModelAttribute("passwordResetForm")
    public PasswordResetForm passwordResetForm() {
        return new PasswordResetForm();
    }

    @GetMapping("/reset-password")
    public String showResetPasswordForm(final Model model) {
        return "reset-password";
    }

    @PostMapping("/reset-password-result")
    public String resetPassword(@Valid final PasswordResetForm passwordResetForm,
                                final BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        return "reset-password";
    }
        // reset password logic
        return "redirect:/reset-password-success";
    }
}

and part of Thymeleaf page:

<form th:action="@{/reset-password-result}" th:object="${passwordResetForm}" method="post">
    <div>
        <div class="input-group">
            <input id="password"
                   class="form-input"
                   placeholder="Set a new password"
                   type="password"
                   th:field="*{password}"/>
        </div>
        <div th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></div>
    </div>
    <div>
        <div class="input-group">
            <input id="confirmPassword"
                   class="form-input"
                   placeholder="Re-type a new password"
                   type="password"
                   th:field="*{confirmPassword}"/>
        </div>
        <div th:if="${#fields.hasErrors('confirmPassword')}" th:errors="*{confirmPassword}"></div>
    </div>
    <div class="form-group">
        <button type="submit" class="form-btn">SET</button>
    </div>
</form>

And now, when I enter different passwords in both inputs, I get the following message in terminal:

2021-03-26 11:13:39.315  WARN 1340 [nio-8080-exec-7] 
s.w.s.h.AbstractHandlerExceptionResolver : Resolved 
[org.springframework.validation.BindException: 
org.springframework.validation.BeanPropertyBindingResult: 1 errors
Error in object 'passwordResetForm': codes 
[FieldsValueMatch.passwordResetForm,FieldsValueMatch]; arguments 
[org.springframework.context.support.DefaultMessageSourceResolvable: codes [passwordResetForm.,]; arguments []; default message [],password,confirmPassword]; default message [Passwords do not match]]

and instant 400 result code. My resetPassword method in the Controller is not called, so instead my Thymeleaf page I get Whitelabel Error Page. The same happens when I put password shorter than 8 characters. What am I doing wrong?

I'd appreciate your help!


Solution

  • thanks for all the replies. For no reason, it just started to work without any change from my side... I don't know what happened, maybe I needed to restart my web browser or something like this...