LoginSignup
0
1

More than 3 years have passed since last update.

【Spring】カスタムバリデーション作ってみた

Last updated at Posted at 2020-12-27

開発中にカスタムバリデーションを見かけたので、理解のために自分でも作ってみました。
以下の3つを作ります。

  1. シンプルなもの
  2. パスワードの一致チェック
  3. @Size的なものを自作

参考サイト

Spring カスタムアノテーションに出会った話
Spring書き込み編.独自のバリデーションを作って入力チェックをする。
Jakarta EE 8 仕様 API

まずは文字数チェックのみのシンプルなのから

簡単なのから作ります。
5文字以上入力すればOKというバリデーションです。

アノテーションを作る

MailCheck.java
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MailCheckImpl.class)
public @interface MailCheck {

    String message() default "";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}
  • @Targetでアノテーションをつける対象を設定。
  • @Retentionでアノテーションのアノテーションをどの期間保持するかを設定。
  • @Constraintでバリデーションを実装するクラスを設定。
  • @interfaceをつけるとこのクラスをアノテーションとして使えるようになります。
  • message() エラーメッセージ。こういうの→@MailCheck(message={"5文字以上入力してください。"})
  • gropus() バリデーションをグループ化する。@MailCheck(groups={XXX.class})
  • payload() メタ情報を与える。アノテーションをつける対象をカテゴライズするのに使うようです。@MailCheck(payload={XXX.Marker.class})
MailCheckImpl.java
public class MailCheckImpl implements ConstraintValidator<MailCheck, String> {

    private MailCheck mailCheck;

    //初期化処理。
    @Override
    public void initialize(MailCheck annotation) {
        this.mailCheck = annotation;
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value.length() >= 5) {
            return true;
        }
        return false;
    }
}

バリデーションの実装クラスです。

  • ConstraintValidator<A, T> Aに作成したインターフェース(MailCheck)、Tにアノテーションをつける対象クラス(String)を指定。
  • initialize() 初期化処理。
  • isVlid() バリデーション処理を実装します。5文字以上入力したらOKという内容にしました。

アノテーションを使う

Mail.java
@Data
public class Mail {

    @MailCheck(message = "5文字以上入力して下さい。")
    private String mail;

    @MailCheck(message = "5文字以上入力して下さい。")
    private String mailConfirm;
}

アノテーションをFormクラスに付けます。
今回は対象がFIELD・StringなのでStringのフィールドに付けています。

MailController.java
@Controller
public class MailController {

    @GetMapping("/hello")
    public String getHello(@ModelAttribute Mail mail) {
        return "hello";
    }

    @PostMapping("/mail")
    public String getPassword(Model model, @Valid Mail mail, BindingResult result) {

        if (result.hasErrors()) {
            return "hello";
        }
        return "success";
    }
}

引数で@Valid Formクラスを渡します。

hello.html
<body>
<form method="POST" action="/mail" name="nameform">
        <p>・メール <input type="text" name="mail"> <span
            th:if="${#fields.hasErrors('mail.mail')}"
            th:errors="*{mail.mail}"></span>

        <p>・メール確認<input type="text" name="mailConfirm">
        <span th:if="${#fields.hasErrors('mail.mailConfirm')}"
            th:errors="*{mail.mailConfirm}"></span>

        <br><input type="submit" value="送信">
    </form>
</body>

結果はこんな感じ↓
キャプチャ.PNG

メールアドレス一致チェックを作ってみる

MailSameCheck.java
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MailSameCheckImpl.class)
public @interface MailSameCheck {

    String message() default "デフォルトメッセージ";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

@Target({ ElementType.TYPE }) は対象がクラス・インタフェース・アノテーションです。

MailSameCheckImpl.java
public class MailSameCheckImpl implements ConstraintValidator<MailSameCheck, Mail> {

    private MailSameCheck mailSameCheck;

    @Override
    public void initialize(MailSameCheck annotation) {
        this.mailSameCheck = annotation;
    }

    @Override
    public boolean isValid(Mail value, ConstraintValidatorContext context) {

        //デフォルトのエラーメッセージを無効にする。
        context.disableDefaultConstraintViolation();

        if (value.getMail().isBlank() | value.getMailConfirm().isBlank()) {

            //時間的な制約を付けたい時に使うらしい。今回は紹介のみで使いません。
            ClockProvider cp = context.getClockProvider();

            //エラーメッセージの変更 & メッセージ表示対象をmailのみにする
            context.buildConstraintViolationWithTemplate("メールが入力されていません。")
                    .addPropertyNode("mail").addConstraintViolation();
            return false;
        }

        if (!value.getMail().equals(value.getMailConfirm())) {

            //エラーメッセージの変更 & メッセージ表示対象をmailConfirmのみにする
            context.buildConstraintViolationWithTemplate("メールが一致しません。")
                    .addPropertyNode("mailConfirm").addConstraintViolation();
            return false;
        }
        return true;
    }
}
  • buildConstraintViolationWithTemplate() エラーメッセージの変更。他にも色々できるっぽいですが、よくわかりませんでした。詳しい方よかったら教えてください。
  • addPropertyNode() エラーメッセージを出力したいフィールド名を指定する。
  • addConstraintViolation() ConstraintViolation を作成するには、このメソッドを呼び出す必要があります。

今回やっているのは
メールアドレスが未入力の時はmailフィールドだけに"メールが入力されていません。"と表示する。
メールアドレスが一致しない時はmailConfirmフィールドだけに"メールが一致しません。"と表示する。

Mail.java
@Data
@MailSameCheck
public class Mail {
    private String mail;
    private String mailConfirm;
}

今度は対象が@Target({ ElementType.TYPE })(クラス・インタフェース・アノテーション)なのでアノテーションをクラスの頭に付けます。
MailController.javaは変更なし。

実行するとこんな感じになります。↓
mail2.PNG

@Size的なものを作る

何となく作りたくなったので作ります。

Sizeppoino.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

import com.example.demo.Sizeppoino.List;

@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(List.class)
@Constraint(validatedBy = SizeppoinoImpl.class)
public @interface Sizeppoino {

    String message() default "1文字以上10文字以下で入力してください。";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    int max() default 10;

    int min() default 1;

    //1つのフィールドにアノテーションを複数付けられる
    @Target({ ElementType.FIELD })
    @Retention(RetentionPolicy.RUNTIME)
    @interface List {

        Sizeppoino[] value();

    }
}
  • int max() 入力文字の上弦設定用。
  • int min() 入力文字の下弦の設定用。
  • @interface List{A[] value();} で1つのフィールドにアノテーションを複数付けられるようです。使い所はわかりません。
SizeppoinoImpl.java
public class SizeppoinoImpl implements ConstraintValidator<Sizeppoino, String> {
    int max;
    int min;

    @Override
    public void initialize(Sizeppoino annotation) {
        this.max = annotation.max();
        this.min = annotation.min();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value.length() > this.max | value.length() < this.min) {
            return false;
        }
        return true;
    }
}
Mail.java
@Data
public class Mail {

    @Sizeppoino(min = 3, max = 15, message = "3文字以上15文字以下で入力してください")
    @Sizeppoino(min = 6, max = 20, message = "6文字以上20文字以下で入力してください")
    private String mail;

    @Sizeppoino(min = 3, max = 15, message = "3文字以上15文字以下で入力してください")
    private String mailConfirm;
}

MailController.javaは変更なし。

実行結果↓
mail3.PNG

まとめ

よく使うものをカスタムバリデーションとして作っておくと便利ですね。
メールやパスワードの一致チェック、メールの2重登録チェックなんかもあると色々な所で使えそうです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1