エキサイト株式会社 メディア開発の佐々木です。
現在、SpringBootで2つ以上のプロパティをチェックするカスタムバリデーションを共有します。
アノテーションの定義
下記のようにカスタムアノテーションを定義します。
(@Constraint(validatedBy = NotBlankAny.NotBlankAnyValidator.class)
ここの部分はエラーになります)
@Documented @Target(value = {ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = NotBlankAny.NotBlankAnyValidator.class) public @interface NotBlankAny { String message() default "please {fields} not empty."; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; String[] fields(); }
バリデーション定義
次にバリデーション部分を定義します。
class NotBlankAnyValidator implements ConstraintValidator<NotBlankAny,Object> { private String[] fields; @Override public void initialize(NotBlankAny constraintAnnotation) { this.fields = constraintAnnotation.fields(); // ※1 } @Override public boolean isValid(Object value, ConstraintValidatorContext context) { if (Objects.isNull(value)) { return false; } BeanWrapperImpl beanWrapper = new BeanWrapperImpl(value); return Stream.of(fields).allMatch(e -> StringUtils.isNotBlank((String)beanWrapper.getPropertyValue(e))); // ※2 } }
※1
でアノテーション内で指定されたフィールドの文字列が配列で入ってきます。※2
で一つずつ取り出してチェックします。
使い方
対象のデータクラスに、 @NotBlankAny(fields = {"フィールド名1","フィールド名2"})
を定義すると、そのデータを使ってバリデーションがかかります。
@RestController @RequestMapping @RequiredArgsConstructor @Slf4j public class DemoController { @GetMapping public Mono index(@Valid Form form) { return Mono.defer(() -> Mono.just(form)); } @Data @NotBlankAny(fields = {"firstName","lastName"}) // こんな感じで設定します static class Form { private String firstName; private String lastName; } }
これでチェックができるので、再利用性はかなり高いバリデーションができると思います。
最後に
フィールド複数のバリデーションを使いたいときって結構あると思います。メールアドレスの再入力とか、パスワードを同じもの2つ入れるとか。実際にチェックのところは、一致や異なるものやどれかひとつみたいなものは、バリデーション側での実装でバリエーションは増やせると思います。
宣伝
エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。長期インターンも歓迎していますので、興味があれば連絡いただければと思います。
おまけ
全体のコード
@Documented @Target(value = {ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = NotBlankAny.NotBlankAnyValidator.class) public @interface NotBlankAny { String message() default "One of {fields} must not be empty"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; String[] fields(); class NotBlankAnyValidator implements ConstraintValidator<NotBlankAny,Object> { private String[] fields; @Override public void initialize(NotBlankAny constraintAnnotation) { this.fields = constraintAnnotation.fields(); } @Override public boolean isValid(Object value, ConstraintValidatorContext context) { if (Objects.isNull(value)) { return false; } BeanWrapperImpl beanWrapper = new BeanWrapperImpl(value); return Stream.of(fields).allMatch(e -> StringUtils.isNotBlank((String) beanWrapper.getPropertyValue(e))); } } }