Я на практике использую следующий подход: для каждой хранимой сущности, которую может создать/изменить пользователь, я создаю класс, описывающий форму (иногда бывает, что для редактирования и создания используются даже разные классы в виду разного набора полей).
Это делается по двум причинам: во-первых - не все поля в хранимой сущности пользователи могут создавать/изменять (например, даты создания/редактирования и идентификаторы), во-вторых - вынесение аннотаций валидации из класса сущности.
Теперь ближе к делу: для валидации существуют аннотации из пакета javax.validation.constraints, а также дополнительные, предоставляемые реализациями Bean Validation API (например @Email из Hibernate Validator).
Допустим, у нас есть класс, представляющий собой форму регистрации:
SignUpForm.javapublic class SignUpForm {
@Email
private String email;
@Size(min = 8)
@Pattern(regexp = "какая-нибудь регулярка для проверки надёжности пароля")
private String password;
}
В контроллере мы будем принимать форму таким образом:
SignUpController.javapublic class SignUpController {
public ResponseEntity signUp(@RequestBody @Valid SignUpForm form, BindingResult bindingResult) {
}
}
В
form будут содержаться данные, отправленные пользователем, а в
bindingResult - результат валидации, который можно легко проверить, вызвав
bindingResult.hasErrors(). Отмечу только то, что аргумент класса BindingResult должен быть сразу после аргумента, помеченного аннотациями @RequestBody и
valid, в других случаях валидация работать не будет.
Но дальше - больше. Допустим, нам нужно добавить поле для подтверждения пароля и проверять его соответствие паролю. В таком случае нам надо добавить свою аннотацию (в данном случае на класс) и валидатор.
Новый класс формы:
SignUpForm.java@PasswordMatch
public class SignUpForm {
@Email
private String email;
@Size(min = 8)
@Pattern(regexp = "какая-нибудь регулярка для проверки надёжности пароля")
private String password;
private String passwordConfirmation;
}
Аннотация:
PasswordMatch.java@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordMatchValidator.class)
@Documented
public @interface PasswordMatch {
String message() default "{constraints.passwordmatch}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented
@interface List {
PasswordMatch[] value();
}
}
Ну и валидатор:
PasswordMatchValidator.javapublic class PasswordMatchValidator implements ConstraintValidator<PasswordMatch, SignUpForm> {
@Override
public void initialize(PasswordMatch constraintAnnotation) {
}
@Override
public boolean isValid(SignUpForm value, ConstraintValidatorContext context) {
return Objects.equals(value.getPassword(), value.getPasswordConfirmation());
}
}
И ещё один способ валидации - при помощи спринговых валидаторов. Допустим, нам нужно проверить существование указанного email в нашей БД, что вполне вписывается в процесс валидации. В таком случае пишем валидатор:
SignUpFormValidator.javapublic class SignUpFormValidator implements Validator {
public boolean supports(Class<?> type) {
return SignUpForm.class.isAssignableFrom(type);
}
public void validate(Object target, Errors errors) {
SignUpForm form = (SignUpForm) target;
// проверяем, что нужно и добавляем ошибки в errors, если они есть
}
}
Регистрируем его в контроллере:
SignUpController.javapublic class SignupController {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.addValidators(new SignUpFormValidator());
}
}
После этого Spring будет производить дополнительную валидацию при помощи этого валидатора.
Более подробно с валидацией в Spring можно почитать в
официальной документации.