@ModelAttribute

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

  • Java

  • Kotlin

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

請求參數是一個 Servlet API 概念,包括來自請求 Body 的表單資料和查詢參數。URI 變數和標頭也包含在內,但前提是它們不會覆蓋具有相同名稱的請求參數。標頭名稱中的破折號會被移除。

上面的 Pet 實例可能是

  • 從模型中存取,模型可能是由 @ModelAttribute 方法 新增的。

  • 如果模型屬性在類別層級的 @SessionAttributes 註解中列出,則可從 HTTP Session 存取。

  • 如果模型屬性名稱與請求值 (例如路徑變數或請求參數) 的名稱相符 (範例如下),則透過 Converter 取得。

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

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

如上所述,如果模型屬性名稱與請求值 (例如路徑變數或請求參數) 的名稱相符,並且存在相容的 Converter<String, T>,則可以使用 Converter<String, T> 來取得模型物件。在下面的範例中,模型屬性名稱 account 與 URI 路徑變數 account 相符,並且有一個已註冊的 Converter<String, Account>,它可能會從持久性儲存區中檢索它

  • Java

  • Kotlin

@PutMapping("/accounts/{account}")
public String save(@ModelAttribute("account") Account account) { (1)
	// ...
}
@PutMapping("/accounts/{account}")
fun save(@ModelAttribute("account") account: Account): String { (1)
	// ...
}

預設情況下,建構子和屬性 資料繫結 都會應用。但是,模型物件設計需要仔細考慮,並且出於安全考量,建議使用專門為 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)。

在某些情況下,您可能希望在不進行資料繫結的情況下存取模型屬性。對於這種情況,您可以將 Model 注入到控制器中並直接存取它,或者,設定 @ModelAttribute(binding=false),如下例所示

  • Java

  • Kotlin

@ModelAttribute
public AccountForm setUpForm() {
	return new AccountForm();
}

@ModelAttribute
public Account findAccount(@PathVariable String accountId) {
	return accountRepository.findOne(accountId);
}

@PostMapping("update")
public String update(AccountForm form, BindingResult result,
		@ModelAttribute(binding=false) Account account) { (1)
	// ...
}
1 設定 @ModelAttribute(binding=false)
@ModelAttribute
fun setUpForm(): AccountForm {
	return AccountForm()
}

@ModelAttribute
fun findAccount(@PathVariable accountId: String): Account {
	return accountRepository.findOne(accountId)
}

@PostMapping("update")
fun update(form: AccountForm, result: BindingResult,
		   @ModelAttribute(binding = false) account: Account): String { (1)
	// ...
}
1 設定 @ModelAt\tribute(binding=false)

如果資料繫結導致錯誤,則預設情況下會引發 MethodArgumentNotValidException,但您也可以在 @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 @ModelAttribute 旁邊新增 BindingResult
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String { (1)
	if (result.hasErrors()) {
		return "petForm"
	}
	// ...
}
1 @ModelAttribute 旁邊新增 BindingResult

您可以透過新增 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 驗證 Pet 實例。
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String { (1)
	if (result.hasErrors()) {
		return "petForm"
	}
	// ...
}
1 驗證 Pet 實例。

如果在 @ModelAttribute 之後沒有 BindingResult 參數,則會引發帶有驗證錯誤的 MethodArgumentNotValueException。但是,如果方法驗證適用,因為其他參數具有 @jakarta.validation.Constraint 註解,則會改為引發 HandlerMethodValidationException。有關更多詳細資訊,請參閱 驗證 章節。

使用 @ModelAttribute 是選用的。預設情況下,任何非簡單值型別 (由 BeanUtils#isSimpleProperty 確定) 未被任何其他引數解析器解析的參數,都會被視為隱含的 @ModelAttribute
當使用 GraalVM 編譯為原生映像時,上述隱含的 @ModelAttribute 支援不允許對相關資料繫結反射提示進行適當的預先 (ahead-of-time) 推斷。因此,建議明確使用 @ModelAttribute 註解方法參數,以便在 GraalVM 原生映像中使用。