解決法
自分で考えた解
th:object
に入れる変数を、バリデートを設定しているクラス名(キャメルケース)に合わせる
コメントでいただいた解法
「追記:コメントから」の項目とコメント参照
事象
- 以下1~3を実装し、バリデートにかかる状態で「登録」を押すと例外が発生してしまった
- 例外は
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'newForm' available as request attribute
(newFormがダメなようなエラー)
修正前の実装
1. Formクラス
- nameに「1以下のサイズを許可」するバリデートを設定
@Data
public class SampleForm {
@Size(max = 1)
private String name;
}
2. Controllerクラス
- 1.のFormクラスを使って画面表示と入力内容の取得をしている
- Formクラスは「newForm」という属性名になっている
//新規登録画面を表示
@GetMapping("/sample")
public String displayadd(Model model) {
model.addAttribute("newForm", new SampleForm());
return "common/sample";
}
//新規登録処理
@PostMapping("/sample/create")
public String createUser(@Validated SampleForm newForm, BindingResult result, Model model) {
if (result.hasErrors()) {
// バリデートにかかったら元の画面を表示
return "common/sample";
}
// 新規登録する処理を入れる
return "complete";
}
3. 新規登録画面html(thymeleaf)
<form th:object="${newForm}">
<div>
<input type="text" th:field="*{name}">
<div th:errors="*{name}"></div>
</div>
<input type="submit" th:formaction="@{/sample/create}" th:formmethod="post" value="登録">
</form>
修正方法
- 以下のように修正することで正常に動くようになった
- 「3.新規登録画面html」の
th:object="${newForm}"
をth:object="${sampleForm}"
に修正 - 「2.Controllerクラス」の
newForm
をsampleForm
に修正
- 「3.新規登録画面html」の
- ★別解
- 「3.新規登録画面html」のエラーメッセージを出す部分を、以下の通り「sampleFormというオブジェクトからとる」「sampleForm」がない時は表示しない、となる変更をす
<div th:if="${sampleForm}" th:errors="${sampleForm.name}"></div>
- 「2.Controllerクラス」新規登録処理のif内に、
model.addAttribute("newForm", newForm);
を追加
- 「3.新規登録画面html」のエラーメッセージを出す部分を、以下の通り「sampleFormというオブジェクトからとる」「sampleForm」がない時は表示しない、となる変更をす
事象整理
- th.errorsの値は、
${バリデート設定しているFormクラスの名前(キャメルケース).フィールド名}
にする必要がある。 - 別解の「2.Controllerクラス」のみを対応すると、例外は出ないがエラーメッセージも出ない状態になった
- 「★別解」のときに、
th:if="${sampleForm}"
を入れないと、新規登録画面表示時に例外が発生する。(修正前の登録押下時の例外と同じ種類で、sampleFormでダメなようなエラー) - 「★別解」で「2.Controllerクラス」のほうの修正がないと、修正前と同じエラーが同じタイミングで発生する
考察
- バリデーションエラーにかかるとき、BindingResultか何かで
model.addAttribute("{Formクラスの名前(キャメルケース)}", {入力情報やバリデートエラーメッセージ});
的なのが自動で設定されているのではないか?と思った - 画面表示時に渡している
model.addAttribute
の属性名が「Formクラス名(キャメルケース)」と等しければ、登録処理から帰って来る時と同じ属性名になるので、入力値が保持されるしエラーメッセージもちゃんと出る
追記:コメントから
下記コメントで原因を理解しました。
- リクエストパラメータの引数の前に
@ModellAttribute("任意の属性名")
をつけると、そのリクエストパラメータに対してモデル属性名を付けられる - エラーが起きていた時のように、このアノテーションをつけなければルールに沿って自動で命名されていた
原因
この場合のエラーの直接の原因はcreateUserの引数に「newForm」という名前で指定された@ModelAttributeが存在しないことです。
解決法
@ModellAttributeはリクエストのパラメーターと引数のオブジェクトの変換を指定します。
失敗していた時に起きていたこと
@ModelAttributeを指定されていないとき、Springが規約に従って暗黙的に変換していた