@ModelAttribute

@ModelAttribute 方法參數註解將表單資料、查詢參數、URI 路徑變數和請求標頭繫結到模型物件上。例如

  • Java

  • Kotlin

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) { } (1)
1 繫結到 Pet 的實例。
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute pet: Pet): String { } (1)
1 繫結到 Pet 的實例。

表單資料和查詢參數優先於 URI 變數和標頭,只有在它們不覆蓋同名的請求參數時才會包含 URI 變數和標頭。標頭名稱中的破折號會被移除。

Pet 實例可能是:

  • 從模型存取,模型可能已由 Model 新增。

  • 如果模型屬性已在類別層級的 @SessionAttributes 中列出,則從 HTTP 會話存取。

  • 透過預設建構子實例化。

  • 透過「主要建構子」實例化,其引數與 Servlet 請求參數相符。引數名稱透過位元組碼中執行階段保留的參數名稱來決定。

預設情況下,會同時應用建構子和屬性資料繫結。但是,模型物件設計需要仔細考慮,並且基於安全考量,建議使用專門為 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].nameaccount[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";
	}
	// ...
}
1 新增 BindingResult
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String { (1)
	if (result.hasErrors()) {
		return "petForm"
	}
	// ...
}
1 新增 BindingResult

若要使用 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 ValidationSpring validation)。例如

  • 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 判斷的簡單值類型 *AND* 且未由任何其他引數解析器解析的引數,都會被視為隱含的 @ModelAttribute
使用 GraalVM 編譯為原生映像時,上述隱含的 @ModelAttribute 支援不允許正確的預先編譯時推斷相關的資料繫結反射提示。因此,建議明確地使用 @ModelAttribute 註解方法參數,以便在 GraalVM 原生映像中使用。