@ModelAttribute

@ModelAttribute メソッドパラメーターアノテーションは、フォームデータ、クエリパラメーター、URI パス変数、リクエストヘッダーをモデルオブジェクトにバインドします。例:

  • Java

  • Kotlin

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) { } (1)
1Pet のインスタンスにバインドします。
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute pet: Pet): String { } (1)
1Pet のインスタンスにバインドします。

フォームデータとクエリパラメーターは URI 変数とヘッダーよりも優先されます。これらは、同じ名前のリクエストパラメーターを上書きしない場合にのみ含まれます。ヘッダー名からダッシュは削除されます。

Pet インスタンスは次のとおりです。

  • Model によって追加された可能性があるモデルからアクセスされます。

  • モデル属性がクラスレベルの @SessionAttributes にリストされている場合は、HTTP セッションからアクセスされます。

  • デフォルトのコンストラクターを通じてインスタンス化されます。

  • サーブレットリクエストパラメーターに一致する引数を持つ「プライマリコンストラクター」を通じてインスタンス化されます。引数名は、バイトコード内でランタイムに保持されるパラメーター名によって決定されます。

デフォルトでは、コンストラクターとプロパティデータバインディングの両方が適用されます。ただし、モデルオブジェクトの設計には慎重な検討が必要であり、セキュリティ上の理由から、Web バインディング専用にカスタマイズされたオブジェクトを使用するか、コンストラクターバインディングのみを適用することをお勧めします。プロパティバインディングを引き続き使用する必要がある場合は、設定できるプロパティを制限するために allowedFields パターンを設定する必要があります。これに関する詳細と構成例については、モデル設計を参照してください。

コンストラクターバインディングを使用する場合、@BindParam アノテーションを通じてリクエストパラメーター名をカスタマイズできます。例:

  • Java

  • Kotlin

class Account {

    private final String firstName;

	public Account(@BindParam("first-name") String firstName) {
		this.firstName = firstName;
	}
}
class Account(@BindParam("first-name") val firstName: String)
@BindParam は、コンストラクターパラメーターに対応するフィールドに配置することもできます。@BindParam はそのままサポートされていますが、DataBinder に DataBinder.NameResolver を設定することで別のアノテーションを使用することもできます。

コンストラクターバインディングは、ListMap、単一の文字列 (たとえば、カンマ区切りリスト) から変換された配列引数、または accounts[2].name や account[KEY].name などのインデックスキーに基づく配列引数をサポートします。

WebFlux は、Spring MVC とは異なり、モデル内のリアクティブ型 (例: Mono<Account>) をサポートします。リアクティブ型ラッパーの有無にかかわらず、@ModelAttribute 引数を宣言することができ、実際の値に応じて解決されます。

データバインディングでエラーが発生した場合、デフォルトで WebExchangeBindException が発生しますが、コントローラーメソッドでそのようなエラーを処理するために、@ModelAttribute のすぐ隣に BindingResult 引数を追加することもできます。例:

  • Java

  • Kotlin

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { (1)
	if (result.hasErrors()) {
		return "petForm";
	}
	// ...
}
1BindingResult を追加します。
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String { (1)
	if (result.hasErrors()) {
		return "petForm"
	}
	// ...
}
1BindingResult を追加します。

BindingResult 引数を使用するには、リアクティブ型ラッパーを使用せずに、その前に @ModelAttribute 引数を宣言する必要があります。リアクティブを使用する場合は、リアクティブを使用してエラーを直接処理できます。例:

  • Java

  • Kotlin

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public Mono<String> processSubmit(@Valid @ModelAttribute("pet") Mono<Pet> petMono) {
	return petMono
		.flatMap(pet -> {
			// ...
		})
		.onErrorResume(ex -> {
			// ...
		});
}
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") petMono: Mono<Pet>): Mono<String> {
	return petMono
			.flatMap { pet ->
				// ...
			}
			.onErrorResume{ ex ->
				// ...
			}
}

jakarta.validation.Valid アノテーションまたは Spring の @Validated アノテーションを追加することで、データバインディング後に検証を自動的に適用できます ( Bean バリデーションおよび Spring 検証を参照)。例:

  • Java

  • Kotlin

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { (1)
	if (result.hasErrors()) {
		return "petForm";
	}
	// ...
}
1 モデル属性引数で @Valid を使用します。
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String { (1)
	if (result.hasErrors()) {
		return "petForm"
	}
	// ...
}
1 モデル属性引数で @Valid を使用します。

他のパラメーターに @Constraint アノテーションがあるためにメソッド検証が適用される場合は、代わりに HandlerMethodValidationException が発生します。コントローラーメソッド検証のセクションを参照してください。

@ModelAttribute の使用はオプションです。デフォルトでは、BeanUtils#isSimpleProperty (Javadoc) によって決定される単純な値型ではなくかつ他の引数リゾルバーによって解決されない引数は、暗黙的な @ModelAttribute として扱われます。
GraalVM を使用してネイティブイメージにコンパイルする場合、上記の暗黙的な @ModelAttribute サポートでは、関連するデータバインディングのリフレクションヒントを適切に事前推論できません。結果として、GraalVM ネイティブイメージで使用するためにメソッドパラメーターに @ModelAttribute のアノテーションを明示的に付けることをお勧めします。