組込のバリデーション
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; } } }