はじめに
Spring Bootの開発環境で、フォームデータのバリデーションとエラーメッセージの表示を行う処理を実装中、BindingResult
でバリデーション結果を保持するようにしたところ、IllegalStateException
が発生した件について、原因と解決方法をまとめました。
1.エラー内容
まず、実装内容について簡単に記載します。
以下のように、単項目のGETパラメータをInteger
型で受け取り、BindingResultに結果を保持するよう実装していました。
@RequestMapping(value = {"/sample/"}, method = RequestMethod.GET)
public String sampleComplete(@RequestParam(name = "sampleCode", required = false) Integer sampleCode,
BindingResult bindingResult, Model model) {
// 実装
return "/sample";
}
しかし、実行したところ、以下のエラーが発生しました。
java.lang.IllegalStateException: An Errors/BindingResult argument is expected to be declared immediately after the model attribute,
the @RequestBody or the @RequestPart arguments to which they apply: public
java.lang.String SampleController.sampleComplete(java.lang.Integer,
org.springframework.validation.BindingResult, org.springframework.ui.Model)
throws java.lang.Exception
「BindingResult引数が対応するモデル属性、@RequestBodyなどの引数の直後に宣言されていない」という内容のエラーですが、SampleController.java
では直後に宣言しているので、別の部分に原因がありそうです。
BindingResultは、バリデーションエラーを保持するためのものであり、対応する引数の直後に配置する必要があります。
2.原因
原因を調査したところ、以下の理由からBindingResult
は@RequestParamに対応していないことが分かりました。
BindingResult
は、Formオブジェクトに対してバリデーション結果を保持するよう設計されている
つまり、複数フィールドをもつFormオブジェクトを引数とすべきところで、単一のリクエストパラメータを記載していたことが間違いだったようです。
※@RequestParam:単一のリクエストパラメータをバインドするためのものであり、複数フィールドを持つオブジェクトのバインドには適していない
3.解決
解決方法は、2つあります。
①そもそもBindingResult
は使わない
②BindingResult
を使用する場合は、単項目パラメータでもFormオブジェクトを作成する
①で解決も可能ですが、今回は他処理に合わせた仕様としたかったので、②の解決方法をとりました。
@ModelAttributeを使用して、Integer
ではなくSampleForm
というFormオブジェクトに格納するよう修正した結果が以下です。
@RequestMapping(value = {"/sample/"}, method = RequestMethod.GET)
public String sampleComplete(@ModelAttribute SampleForm sampleForm,
BindingResult bindingResult, Model model) {
// 実装
return "/sample";
}
これで実行結果も問題なく、バリデーション結果が保持されていました!
あとがき
今回のエラーは、BindingResult
がFormオブジェクトに対してバリデーション結果を保持するものであることを把握していなかったために発生しました。
知らずにこのエラーが発生してしまったという方の参考になれば幸いです。