組込のバリデーション
Spring MVC では JSR-303 のバリデーションが使える。
@NotNull
@Size(min=1, max=50)
@Pattern(regexp = "[a-zA-Z-_.]+@[a-zA-Z-\\.]+")
とかのバリデーションをBeanのフィールドに宣言して、アクションのメソッドで
public String save(Model model, @Valid @ModelAttribute("form") HogeForm form, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
model.addAttribute("form", form);
return "hoge/edit";
}
こんな感じにエラー分岐すれば、form:errorsタグでエラーが表示できる。
<form:errors path="email" cssClass="text-error" />
独自のバリデーション
標準のバリデーションで足りない分をどうすすか。
最初は org.springframework.validation.Validator を実装して、アクションの頭で
ValidationUtils.invokeValidator(hogeValidator, form, bindingResult);
とかやってみたけど、これはきれいじゃない。
調べたら、独自のアノテーションを定義できるらしい。
以下のように ConstraintValidator と、ペアになるアノテーションをセットで定義してやればOK
/**
* JSR-303 Validation Annotation for List empty check
*/
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=ListNotEmpty.ListNotEmptyValidator.class)
public @interface ListNotEmpty {
public String message() default "{ListNotEmpty}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Validator
*/
public static class ListNotEmptyValidator implements ConstraintValidator<ListNotEmpty, List<?>> {
@Override
public void initialize(ListNotEmpty annotation) {
}
@Override
public boolean isValid(List<?> target, ConstraintValidatorContext context) {
if (target == null || target.size() == 0) {
return false;
}
return true;
}
}
}
複数フィールドをチェックする必要がある場合はクラスをバリデーションの対象にすればOK
/**
* JSR-303 Validation Annotation for Password confirmation.
* Target bean should implement PasswordConfirmation.PasswordConfirmable.
* (No need to put PasswordConfirmation annotation)
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=PasswordConfirmation.PasswordConfirmationValidator.class)
public @interface PasswordConfirmation {
public String message() default "{PasswordConfirmation}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Interface for password confirmation
*/
@PasswordConfirmation
public interface PasswordConfirmable {
public String getPassword();
public String getPasswordConfirmation();
}
/**
* Validator
*/
public class PasswordConfirmationValidator implements ConstraintValidator<PasswordConfirmation, PasswordConfirmable> {
@Override
public void initialize(PasswordConfirmation annotation) {
}
@Override
public boolean isValid(PasswordConfirmable bean, ConstraintValidatorContext context) {
if (bean.getPassword() != null && !bean.getPassword().equals(bean.getPasswordConfirmation())) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
.addNode("passwordConfirmation")
.addConstraintViolation();
return false;
}
return true;
}
}
}