I have an issue validating a nested object. What I mean by this is that I have a registration form, where a command object is provided that looks like this
short version:
public class RegistrationObject {
private User user;
@NotEmpty
@Size(min = 5, message = "{password.size}")
@Pattern(regexp =
"^.*(?=.{8,})(?=.*\\d)(?=.*[a-zA-Z])|(?=.{8,})(?=.*\\d)(?=.*[!@#$%^&])|(?=.{8,})(?=.*[a-zA-Z])(?=.*[!@#$%^&]).*$",
message= "{password.strength}")
private String repeatPass;
public RegistrationObject() {
}
public RegistrationObject(User user, String repeatPass) {
this.user = user;
this.repeatPass = repeatPass;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getRepeatPass() {
return repeatPass;
}
public void setRepeatPass(String repeatPass) {
this.repeatPass = repeatPass;
}
}
In my controller I post the object and use @Valid accordingly, it validates the password, but the User object is not being validated. It goes to the service and attempts to be persisted, which throws a validation error. Which is fine, it shows that the constraints work, but validation needs to be happen during form submission.
The question is, why it is not validating the User, and what is a good way to do this kind of validation?
Here are the user, controller method for post and the custom validator
@Entity
@Table(name = "user")
public class User extends BaseEntity implements UserDetails {
@NotEmpty
@Size(min = 5, max = 16, message = "{username.size}")
private String username;
@NotEmpty
@Size(min = 5, message = "{password.size}")
@Pattern(regexp = "^.*(?=.{8,})(?=.*\\d)(?=.*[a-zA-Z])|(?=.{8,})(?=.*\\d)(?=.*[!@#$%^&])|(?=.{8,})(?=.*[a-zA-Z])(?=.*[!@#$%^&]).*$", message= "{password.strength}")
private String password;
@NotEmpty
@Email(message = "{email.valid}")
private String email;
@NotEmpty
@Size(min = 5, max = 16, message = "{firstName.size}")
private String firstName;
@NotEmpty
@Size(min = 5, max = 16, message = "{lastName.size}")
private String lastName;
@NotNull
private int age;
private boolean enabled;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "user")
private Collection<Authorities> authorities = new ArrayList<>();
Controller
@RequestMapping(value = "/signup", method = RequestMethod.POST)
public String register(@Valid @ModelAttribute("regObject") RegistrationObject regObject, BindingResult result) throws PasswordNotMatchException {
if (result.hasErrors()) {
return "registration/signup";
}
userService.register(regObject);
return "redirect:/helloworld/home";
}
@Component("registrationValidator")
public class RegistrationValidator implements Validator {
private UserService userService;
@Autowired
public RegistrationValidator(UserService userService) {
this.userService = userService;
}
@Override
public boolean supports(Class<?> aClass) {
return RegistrationObject.class.equals(aClass);
}
@Override
public void validate(Object obj, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "user.password", "password.empty");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "repeatPass", "password.empty");
RegistrationObject regObj = (RegistrationObject) obj;
if (!regObj.getUser().getPassword().equals(regObj.getRepeatPass())) {
errors.rejectValue("repeatPass", "password.notMatch");
}
User user = userService.findByUsername(regObj.getUser().getUsername());
if (user != null && (user.getUsername().equals(regObj.getUser().getUsername()))) {
errors.rejectValue("username", "username.unique");
}
}
}
What I have tried:
I tried changing the controller method, removing custom validator, which is not ideal and didn't help. Also tried adding @Valid to the registration object user like
@Valid User user
None of them worked.