ti-tomo-knowledge.hatenablog.com
ti-tomo-knowledge.hatenablog.com
上記2つのような流れで、BeanValidationにおける基本的なバリデーションの流れを見ていきましたが、
これらはあくまでBeanValidationやHibernateで事前に用意されたものです。
実際にサービスを作り始めると、チェックしなければならないパターンはもっと多く、また、項目間の値の関係チェック(例えば大小関係)をしなければいけないこともあるでしょう。
今回は、そのようなパターンを2つ用意して、
バリデーションの実装方法について深堀りしていきたいと思います。
1. 項目の値が一意である必要がある場合
例えばアカウント作成時のメールアドレスが挙げられますね。
基本的にメールアドレスは1つのサービスで同一のものが使えないことが多いでしょう。
よって、アカウント作成時に登録済みのメールアドレスを登録しようとしたら、エラーを出す必要があります。
まずは自作のバリデーションを行うためのファイルを2つ用意します。
今回は、Unusedというアノテーションクラスと、実際にバリデーションチェックを行うUnusedValidatorというクラスです。
Unused
package パッケージ名; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Documented @Constraint(validatedBy = {UnusedValidator.class}) // ここは後に作るバリデーションクラスです。 @Target({FIELD}) // 項目に対してバリデーションをかける場合はFIELDを選びます。 @Retention(RUNTIME) public @interface Unused { String message() default "すでに登録済みのメールアドレスです"; // エラーメッセージです。アノテーションの引数にmessageを設定していない場合は、この値が出力されています。 Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; @Target({FIELD}) @Retention(RUNTIME) @Documented public @interface List { Unused[] value(); // インターフェース名[] value()としておいてください } }
UnusedValidator
package パッケージ名; import test.Account; import test.service.AccountService; import org.springframework.beans.factory.annotation.Autowired; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class UnusedValidator implements ConstraintValidator<Unused, String> { @Autowired AccountService accountService; // ここは各自の設定に合わせてください。 public void initialize(Unused constraintAnnotation) { } public boolean isValid(String value, ConstraintValidatorContext context) { Account account = accountService.findByEmail(value); // ここのvalueは入力値になります if(account == null){ return true; } return false; } }
ここでは詳細は省きますが、AccountServiceというサービスクラス内でメールアドレスからユーザを取得できるようにしてあります。
上記で返り値がfalseだった場合、Unusedクラスのmessageに設定しているメッセージが表示されるようになります。
最後にアノテーションの配置ですが、他のバリデーションと同様に、
入力値をチェックしたい項目の上に作成したアノテーションを設置してください。
@Unused private String email;
もちろんgroupsなどのパラメータも設定できますよ。
2. 項目間の一致を比較する場合
アカウント作成時のメールアドレスが挙げられますね。
誤入力を避けるために、入力したパスワードと同じものを再度入力させるパターンも多いでしょう。
よって、アカウント作成時に1つ目と2つ目のメールアドレスの値が異なった場合、エラーを出す必要があります。
先ほどと同様に、まずは自作のバリデーションを行うためのファイルを2つ用意します。
今回は、Confirmというアノテーションクラスと、実際にバリデーションチェックを行うConfirmValidatorというクラスです。
Confirm
package パッケージ名; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; @Documented @Constraint(validatedBy = {ConfirmValidator.class}) @Target({TYPE}) @Retention(RUNTIME) public @interface Confirm { String message() default "2つの入力値が異なります"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; @Target({TYPE}) @Retention(RUNTIME) @Documented public @interface List { Confirm[] value(); } }
ConfirmValidator
※注意して欲しいのは、「ConstraintValidator」の箇所になります。
ここはどの型で引数を受け取るかというところですが、項目間での比較はフォーム全体にバリデーションをかけることになりますので、型はObjectにしてください。ここをObjectにしないと、「No validator could be found for constraint...」などとエラーが発生します。
package パッケージ名; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.util.StringUtils; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.Objects; public class ConfirmValidator implements ConstraintValidator<Confirm, Object> { private String field1; private String field2; private String message; public void initialize(Confirm constraintAnnotation) { field1 = "email1"; // 下記のisValidで使うので、ここでメンバ変数に項目名を入れておいてください。 field2 = "email2"; // ここも同じ message = constraintAnnotation.message(); // Confirmクラスのmessage()です。isValidで使用します。 } public boolean isValid(Object value, ConstraintValidatorContext context) { BeanWrapper beanWrapper = new BeanWrapperImpl(value); Object field1Value = beanWrapper.getPropertyValue(field1); Object field2Value = beanWrapper.getPropertyValue(field2); if (Objects.equals(field1Value, field2Value)) { return true; } else { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(message) // このようにmessageの設定を入れないと、エラー内容が出力されません。 .addPropertyNode(field2Value).addConstraintViolation(); // field2の箇所にエラー内容が出力されるようにしています。 return false; } } }
最後にアノテーションの配置ですが、ここは注意です。
特定の項目のチェックではなく、フォーム全体の中でemail1とemail2の値をチェックするので、
Serializableクラスの上に、アノテーションを設置してください。
@Confirm public class TestForm implements Serializable { private String email1; private String email2; }
こうしないと、フォーム全体から値を引っ張ることができないので、
email1もemail2も取得することができません。
はじめてのSpring Boot―スプリング・フレームワークで簡単Javaアプリ開発 (I・O BOOKS)
- 作者:俊明, 槙
- 発売日: 2016/09/01
- メディア: 単行本