SE_BOKUのまとめノート的ブログ

SE_BOKUが知ってること・勉強したこと・考えたことetc

@Patternでは難しい入力チェックをする独自アノテーションを作る/STS3 +Spring Boot +thymeleaf 

 今回は、「@Patternでは難しいロジックを実装する」方法でオリジナル入力チェックアノテーション定義を作ってみます。

STS3(3.9.6)+SpringBoot2.0+Tymeleaf3.0まで動作確認しています。

f:id:arakan_no_boku:20190222012501j:plain

 

日付をチェックするアノテーションを作ってみる

 

どうせやるなら、役に立つ方がいいので、日付をチェックするアノテーション「@DateValid」を作ります。 

仕様は簡単にするため、西暦で年は4桁のみ許すことにします。 

形式としては「2017/01/31」「2017/1/31」「2017-01-31」「2017-1/31」「20170131」の各パターンをOKにします。

 

まずアノテーションの定義です

 

ソースはこんな感じ。

@Documented
@Constraint(validatedBy={DateValidImp.class})
@Target({FIELD,ANNOTATION_TYPE})
@Retention(RUNTIME)
@ReportAsSingleViolation
public @interface DateValid {
       String message() default "{0}は日付として許可された形式ではありません。";
       Class<?> groups() default {};
       Class<? extends Payload>
payload() default {};

       @Target({FIELD,ANNOTATION_TYPE})
       @Retention(RUNTIME)
       @Documented
        public @interface List {
                DateValid[] value();
        }
}

 

実は、前回の実装しない場合と、する場合のアノテーション定義の違いは、@Constraint(validatedBy={DateValidImp.class}) の部分くらいです。(実装しないときは@Constraint(validatedBy={})でした) 

ここで指定している「DateValidImp.class」が独自ロジックを実装するクラスの名前です。

 

実装クラスです

ソースはこんな感じ。

public class DateValidImp implements ConstraintValidator<DateValid, String> {
      @Override
      public void initialize(DateValid constraintAnnotation) {
       }

     @Override
     public boolean isValid(String value, ConstraintValidatorContext context) {
           if(value == null){
                  return true;
           }
          Pattern ptn = Pattern.compile("^(\\d{4})[-/]?(\\d{1,2})[-/]?(\\d{1,2})$");
         Matcher mch = ptn.matcher(value);
         if(mch.find()){
                try {
                         LocalDate.of(Integer.valueOf(mch.group(1)), Integer.valueOf(mch.group(2)), Integer.valueOf(mch.group(3)));
                 } catch (Exception e) {
                        return false;
                 }
         }else{
               return false;
         }

         return true;
    }

}

 

 実装クラスを作るときの決まりごとです。 

  • ConstraintValidator<T,V> インタフェースを実装します。Tの部分はアノテーション定義、Vはチェックする型のクラスです。今回は文字列なので、Stringにしてます。
  • public void initialize(DateValid constraintAnnotation)を実装します。isValidの前に実行されるので、変数等の初期化をします。
  • public boolean isValid(String value, ConstraintValidatorContext context)を実装します。valueが入力されたFormの値です。ここでチェック処理を行い、OKならtrue、NGならfalseを返します。

入力チェックを実装する時に気をつけるべきことです。

  • NULLチェックを行い、NULLだった場合はtrueを返してください。それが決まりです。なぜなら、NULLチェックは@NotNullを併用して行うことになっているからです。
  • スレッドセーフである必要があります。なので、スレッドセーフでないクラス(例 SimpleDateFormatとか・・)は内部で使えません。

 

ついでなので、チェックを行っている部分もざっと整理しときます。 

正規表現の「"^(\\d{4})[-/]?(\\d{1,2})[-/]?(\\d{1,2})$"」は、\\dが数字にマッチするので、\\d{4}は4桁の数字=西暦、\\d{1,2}は1桁か2桁の数字=月と日ですね。 

それをそれぞれ()でくくっていると、後でgroup(1)で西暦、group(2)で月、group(3)で日が取得できるので、それをIntegerに変換して、LocalDateに渡して日付に変換してます。

 

日付変換が成功すればtrue、失敗すればfalseなので、最も確実な日付入力チェックになります。 

ちなみに、LocalDateはスレッドセーフなDateクラスです。Javaのバージョンによっては使えないので、その場合は、joda-Timeなどを使ってください。SimpleDateFormatとかはだめですよ。

 

作ったアノテーションをためす

 

ここまで実装できたら、後はFormクラスでアノテーションとして付与するだけです。

@DateValid
private String chktest;

public String getChktest() {
       return chktest;
}

public void setChktest(String chktest) {
       this.chktest = chktest;
}

 

実行して、フォーマットとしては通るが、日付としてはおかしい値(例では 2017/1/32)を入力してみます。 

f:id:arakan_no_boku:20170327221756j:plain

いけてますね。 


 STS +Spring Boot+thymeleaf 関連記事

 

入力画面に関連する記事

画像をSUBMITボタン代わりに使う

ラジオボタンとラジオボタングループを使う 

ラジオボタンとラジオボタングループを使う 

チェックボックスを使う。 

HTMLのタグの閉じ忘れで例外が発生する!

プルダウンリストとマルチセレクトボックスを使う。

今度はテキストエリアで複数行入力する。 

テキストボックスの入力と基本的なチェックを使う。

 

参照画面・画面遷移に関連する記事

参照画面:テーブルを使い、行毎に色分けした一覧表を表示する。 

参照画面:条件に一致した時のみHTML要素を出力する。

セッションを使って画面間で情報を受け渡す 

画面遷移:GET時のリクエストパラメータを受け取る 

日本語しか使わなくても、i18N対応はする意味がある。

 

入力チェックに関連する記事

入力チェック:未入力時に空文字が渡される仕様を回避する。 

入力チェック:@Patternと正規表現で独自チェックする。

入力チェック:アノテーション定義を自分で作る。(再利用版)

入力チェック用アノテーション定義を自分で作る。(独自実装版)

入力チェック:複数項目の相関チェックアノテーションの作り方

 

データアクセス・その他に関連する記事

データアクセス:Jprepositoryを使って簡単にCRUDする。 

データアクセス:ネイティブなSQL文を実行する 

クラスパス内の静的ファイルにアクセスする 

SpringBootだとログの書き出しも楽ちんです。 

SpringBootプロジェクトでJUNIT4を使った単体テストをする。