|
对于最新的稳定版本,请使用 Spring Framework 7.0.6! |
@ModelAttribute
您可以将 @ModelAttribute 注解用于方法参数,以从模型中访问属性,或在属性不存在时将其实例化。模型属性还会覆盖与字段名称匹配的查询参数和表单字段的值。这被称为数据绑定,它可避免您处理单独解析和转换查询参数和表单字段的工作。以下示例绑定了一个 Pet 的实例:
-
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 的一个实例。 |
The Pet 实例在前面的示例中解析如下:
-
从模型中如果已经通过
Model添加。 -
从HTTP会话通过
@SessionAttributes。 -
从默认构造函数的调用。
-
从调用带有与查询参数或表单字段匹配的参数的“主构造函数”开始。参数名称通过JavaBeans
@ConstructorProperties或运行时保留的字节码中的参数名称来确定。
在获取模型属性实例后,数据绑定将被应用。WebExchangeDataBinder 类会将查询参数和表单字段的名称与目标 Object 上的字段名称进行匹配。在应用类型转换后,匹配的字段将被填充。有关数据绑定(和验证)的更多信息,请参见
验证。有关自定义数据绑定的更多信息,请参见
DataBinder。
数据绑定可能会导致错误。默认情况下,会抛出一个 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。 |
您可以在数据绑定后自动应用验证,方法是添加jakarta.validation.Valid注解或Spring的@Validated注解(另见Bean Validation和Spring validation)。以下示例使用@Valid注解:
-
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 作为模型属性参数。 |
Spring WebFlux 与 Spring MVC 不同,支持模型中的响应式类型——例如,Mono<Account> 或 io.reactivex.Single<Account>。你可以声明一个 @ModelAttribute 参数,无论是否带有响应式类型包装器,它都会相应地解析为实际值。但是,请注意,要使用 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 ->
// ...
}
}
请注意,使用 @ModelAttribute 是可选的——例如,设置其属性。
默认情况下,任何不是简单值类型(由 BeanUtils#isSimpleProperty 确定)
且未被任何其他参数解析器解析的参数将被视为带有 @ModelAttribute 注解。
在使用GraalVM编译为原生镜像时,上文描述的隐式@ModelAttribute支持无法正确实现相关数据绑定的反射提示的提前推断。因此,建议在GraalVM原生镜像使用场景中,显式标注方法参数为@ModelAttribute。 |