Bean Validationとは
JavaBeanに対して入力チェックを行うためのフレームワーク。
Spring 4.0からはJSR-349のBean Validation 1.1をサポートしています。
以下の話は、実装は、Hibernate Validator 5.2.2のお話。
つい最近の出来事
クライアントからRESTの引数として配列が渡されてくるとき、配列の各要素に桁数チェックを行う、という仕様があった。よくありそうな事案ですよね。
リクエストを受け取る入力フォームは、こんな感じで作っていたわけですよ。
@Digits(integer = 9, fraction = 0) private List<Integer> userIdList;
で、実際に動かしてみると、
1 |
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.Digits' validating type 'java.util.List'. Check configuration for 'userIdList' |
とか言われるわけですよ。
・・・またまた〜、@Validつければ動くんでしょ?ということで、
@Digits(integer = 9, fraction = 0) @Valid private List<Integer> userIdList;
でやってみたら同じエラー。ええ・・こんなよくありそうなチェック、拡張しないとできないの・・・
運用対処方法
仕方がないので、対処方法を考える。
一番簡単な対処方法は、メインフォームに対応するサブフォームを作って@Validと組み合わせる方法。
@Valid private List<UserIdList> userIdList; public class UserIdList { @Digits(integer = 9, fraction = 0) private int userId; }
ただ、これをやるとクライアントでリクエストを送るときのオブジェクトの構造がややこしくなるはず。
試していないけど、こんな感じにしないといけないんじゃなかろうか。とてつもなくアホらしい。
{ userIdList : [ { userId : 123456789 }, { userId : 123456789 }, { userId : 123456789 } ] }
これをやりたくないので、別の方法を考えることに。
どうやって拡張するか
調べてみると、この辺の記事が参考になった。
Support validation for String object in collection #407
Bean Validation 2.0 - A new JSR is born!
Bean ValidationがJava8のTYPE_USEに対応してくれれば、
@Valid private List<@Digits(integer = 9, fraction = 0) Integer> userIdList;
みたいな書き方ができるんだろうけれども、
Javaの下位互換性の関係で、現状のBean Validation 1.1 では対応してくれないみたいなので、
- @DigitsTypeUse、のような独自のアノテーションを作る
- @Digitsに対応するバリデーターの実装を追加する
の2通りが対処案として考えられます。
できれば、アノテーションを使い分けたくないので、@Digitsに対応するバリデーターの実装を追加する方法でやりたかったのだけれども、Hibernate Validatorの実装を見たところそれはできなさそうなので、独自アノテーションを作るしかなさそうです。
参考:Github(ConstraintHelper)
細かくは書きませんが、
- getDefaultValidatorClassesでバリデーターを取得
- Hibernate Validatorで実装されているバリデーターは515行目で固定で取得している
- これがnullでなければ処理を終了するので、既存のアノテーションを拡張したバリデーターを実装しても読み込まれない
という感じ。これを丸ごと差し替えたり拡張したりするのはやり過ぎ感が否めないので、おとなしく独自アノテーションを作成する方が良さそうです。
独自アノテーションを作る
ということで、Listの各要素をチェックするための独自アノテーションを作ってみましょう。
サンプルコードは下記。
基本的には、Bean Validationの@Digitsの実装を模写しているだけです。
Bean Validationのアノテーションは複数組み合わせることができますので、既存の@Digitsと組み合わせた上で、
今回作成するアノテーションはTYPE_USEを用いて型使用箇所に指定できるようにします。
簡単な解説
- 15行目、@TARGET(アノテーションを指定できる箇所)でTYPE_USEを指定する
- 19行目、@Digitsを組み合わせるためアノテーションを付与。
ここで指定するパラメータは、アノテーションを利用するときに設定した値で上書きするため何でも良い - クラスに付与されているそれ以外のアノテーションは、@Digitsと同様
- 23、26行目、@OverridesAttributeすることで、アノテーション使用時に指定する値で上書きするようになる
- それ以外は、@Digitsのものと同様
Listやmessageの設定を変更するならば、適宜変更する(はず、試していないけど)
といった感じです。
参考:Bean Validation specificaion
このアノテーションを利用するときは、前回述べたように
@Valid private List<@DigitsTypeUse(integer = 9, fraction = 0) Integer> userIdList;
のようになります。
早いところ、こんな拡張しなくてもデフォルトで配列やListのチェックができるように次版がリリースされると良いですね。
ていうか、誰かこんなの作って公開している人いそうなもんだけれどもねぇ。
おまけ
BeanValidationのテストコード。
手軽に動作を確認するときは簡単なテストを書くのが一番早いですね。
Bean Validationのテストをするのは少しだけお作法が必要なので、備忘録。