今回は、「@Patternでは難しいロジックを実装する」方法でオリジナル入力チェックアノテーション定義を作ってみます。
STS3(3.9.6)+SpringBoot2.0+Tymeleaf3.0まで動作確認しています。
日付をチェックするアノテーションを作ってみる
どうせやるなら、役に立つ方がいいので、日付をチェックするアノテーション「@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)を入力してみます。
いけてますね。
STS +Spring Boot+thymeleaf 関連記事
入力画面に関連する記事
参照画面・画面遷移に関連する記事
参照画面:テーブルを使い、行毎に色分けした一覧表を表示する。
入力チェックに関連する記事
入力チェック:@Patternと正規表現で独自チェックする。
入力チェック用アノテーション定義を自分で作る。(独自実装版)
データアクセス・その他に関連する記事
データアクセス:Jprepositoryを使って簡単にCRUDする。
SpringBootプロジェクトでJUNIT4を使った単体テストをする。