いつも書き方を忘れて、ネットの海をさ迷っちゃうのでいい加減自分でまとめようと思う。
コードはここです。
Kotlinなのは特に意味はないけれど、定期的に書かないと忘れちゃうからです。
とりあえず入力チェック編
まずは、必須入力チェックをしてみる。
ポイントその1:@NotNullじゃなくて@NotBlankを使うよ
@NotBlank var name: String = ""
ここ、HTMLのtextフォームからフォームクラスにStringで値が入るときには、空""で入ってくる仕様らしい。
なのでnullかどうかを見る、@NotNull
だとバリデートエラーにならなくて、空白か空かどうかも見てくれる、@NotBlank
のほうがよさそう。
ポイントその2:BindingResultは必ず@Validatedの後
@RequestMapping(method = arrayOf(RequestMethod.POST)) fun doPost(@Validated inputForm: InputForm, bindingResult: BindingResult, model: Model): String { return "input/form"; }
BindingResult
には、@Validated
を付けた引数のバリデート結果が入る。
んで、このBindingResult
を@Validated
が付いた引数の後ろに定義するという村の決まりごとがあって、これをしないとエラーでこける。
日本語で表示したい編
これ、エラー表示が英語じゃないですか!
そうですね、日本語で表示しましょう。
resourcesディレクトリの直下にValidationMessages.properties
というファイルを作って、以下を書く。
org.hibernate.validator.constraints.NotBlank.message={0}:必須入力ですよ
んで、もう一度アプリケーション起動して、送信してみる。
org.springframework.context.support.DefaultMessageSourceResolvable: codes [inputForm.name,name]; arguments []; default message [name]:必須入力ですよ
なんかtoStringっぽいのが出ているが、無事指定したメッセージが出でる。
@NotBlank
は、hibernate validatorのアノテーションなので、そのデフォルトのメッセージ定義を上書きしてあげればよい。
ほかのアノテーションの定義は↓から、確認できる。
ポイントその3 プロパティファイルのはエンコーディングはUTF-8
IDEとかで、プロパティファイルを作ると ISO-8859-1でエンコードされたりするんだけど、これだと日本語が文字化けしてうまく出力されない。
ちゃんと、エンコードの設定を確認する。
ポイントその4 {0}を使うときはメッセージ定義ファイルを作る
で、ここ({0}
)の部分。
ほかの解説ページとかだと、プロパティ名(今回の例だとname)が出るって書いてあるんだけど、うまく出てない。
org.springframework.context.support.DefaultMessageSourceResolvable: codes [inputForm.name,name]; arguments []; default message [name]
なぜかはわからんが、メッセージ定義ファイルを作ってあげるとちゃんと出る。
(しばらくなんでか調べたけどよくわからんかった)
外部にメッセージ定義(リソース?)を持つ設定の仕方は、いろんなところに書いてあると思うので説明は割愛。
application.propertiesに以下を設定。
spring.messages.basename=messages spring.messages.cache-seconds=-1 spring.messages.encoding=UTF-8
resourcesディレクトリの直下にmessages.properties
を作る。
(ファイルの名前は設定と一致してればなんでもよさそう)
もう一度起動しなおして、送信。
プロパティ名が出ました。
エラーメッセージにプロパティ名が出たのはいいけど、ユーザからしたらnameって何ぞや状態なので、これも差し替えましょう。
さっき作ったmessages.properties
に、↓を書く。
name=名前
すると、ちゃんとnameのところが差し変わっている。
ちなみに、name
の部分はプロパティ名で、inputForm.name
とかでもちゃんと置換される。
独自のメッセージを表示したい編
おなじ、@NotBlank
エラーでも違うエラーメッセージを項目ごとに出したいことがある。
その時は、次のようにアノテーションを指定する。
InputForm.kt
@NotBlank(message="年齢も恥ずかしがらずにちゃんと入力しよう") var age: String = ""
アノテーションに直接メッセージを埋め込みたくない、几帳面なあなたはValidationMessages.properties
にメッセージを書いて、それのキーを埋め込もう。
InputForm.kt
@NotBlank(message="{E0001}") var age: String = ""
ValidationMessages.properties
E0001=年齢も恥ずかしがらずにちゃんと入力しよう
E0001、業務感がすごくてわくわくする
相関チェックしたい編
独自のチェックだったり、二つ以上の項目についてチェックしたいことがある。
そんな時は、自分でアノテーションを定義して、それを使う。
長々と書いてあるが、isValidがfalseならエラーになる。
それだけ。
public boolean isValid(Object value, ConstraintValidatorContext context) { BeanWrapper beanWrapper = new BeanWrapperImpl(value); String param1 = (String)beanWrapper.getPropertyValue(this.param1Field); String param2 = (String)beanWrapper.getPropertyValue(this.param2Field); context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(message) .addPropertyNode(this.param2Field) .addConstraintViolation(); return Objects.equals(param1, param2); }
なぜここだけJavaなのか…
それはKotlinでアノテーションの書き方がわからなかったからだよ。
悲しい。
ポイントその5 アノテーションは、クラス宣言の上につける
相関チェックみたいに複数のプロパティを対象とするバリデートのアノテーションは、クラスの宣言部分の上につける。
@Equals(param1 = "email1", param2 = "email2") class InputForm {
こんな感じ。