@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 來使用不同的註解 |
建構子繫結支援從單一字串轉換而來的 List
、Map
和陣列引數,例如逗號分隔列表,或基於索引鍵,例如 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";
}
// ...
}
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 Validation 和 Spring 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 原生映像中使用。 |