此版本仍在开发中,尚未被视为稳定版。如需最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

DataBinder

@Controller@ControllerAdvice 类可以包含 @InitBinder 方法,用于初始化 WebDataBinder 实例,这些实例进而可以:spring-doc.cadn.net.cn

@Controller 中,DataBinder 的自定义设置仅在该控制器内部局部生效, 甚至可以通过注解按名称引用,应用于特定的模型属性。 在 @ControllerAdvice 中,自定义设置可应用于全部控制器或其中的一个子集。spring-doc.cadn.net.cn

您可以在DataBinder中注册PropertyEditorConverterFormatter组件以进行类型转换。或者,您可以使用WebFlux 配置在全局共享的FormattingConversionService中注册ConverterFormatter组件。spring-doc.cadn.net.cn

@Controller
public class FormController {

	@InitBinder (1)
	public void initBinder(WebDataBinder binder) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		dateFormat.setLenient(false);
		binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
	}

	// ...
}
1 使用 @InitBinder 注解。
@Controller
class FormController {

	@InitBinder (1)
	fun initBinder(binder: WebDataBinder) {
		val dateFormat = SimpleDateFormat("yyyy-MM-dd")
		dateFormat.isLenient = false
		binder.registerCustomEditor(Date::class.java, CustomDateEditor(dateFormat, false))
	}

	// ...
}
1 使用 @InitBinder 注解。

或者,当通过共享的 Formatter 使用基于 FormattingConversionService 的配置时,您可以采用相同的方法,注册控制器特定的 Formatter 实例,如下例所示:spring-doc.cadn.net.cn

@Controller
public class FormController {

	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); (1)
	}

	// ...
}
1 添加一个自定义格式化器(在本例中为 DateFormatter)。
@Controller
class FormController {

	@InitBinder
	fun initBinder(binder: WebDataBinder) {
		binder.addCustomFormatter(DateFormatter("yyyy-MM-dd")) (1)
	}

	// ...
}
1 添加一个自定义格式化器(在本例中为 DateFormatter)。

模型设计

数据绑定用于 Web 请求时,会将请求参数绑定到模型对象上。默认情况下,请求参数可以绑定到模型对象的任意公共属性,这意味着恶意客户端可能会为模型对象图中存在但不应被设置的属性提供额外的值。因此,模型对象的设计需要仔细斟酌。spring-doc.cadn.net.cn

模型对象及其嵌套的对象图有时也被称为命令对象表单支持对象POJO(Plain Old Java Object,普通旧式Java对象)。

一种良好的做法是使用专用的模型对象,而不是直接暴露你的领域模型(例如 JPA 或 Hibernate 实体)用于 Web 数据绑定。例如,在一个用于修改电子邮件地址的表单中,应创建一个仅声明所需输入属性的ChangeEmailForm模型对象:spring-doc.cadn.net.cn

public class ChangeEmailForm {

	private String oldEmailAddress;
	private String newEmailAddress;

	public void setOldEmailAddress(String oldEmailAddress) {
		this.oldEmailAddress = oldEmailAddress;
	}

	public String getOldEmailAddress() {
		return this.oldEmailAddress;
	}

	public void setNewEmailAddress(String newEmailAddress) {
		this.newEmailAddress = newEmailAddress;
	}

	public String getNewEmailAddress() {
		return this.newEmailAddress;
	}

}

另一种良好的做法是应用 构造函数绑定, 它仅使用构造函数参数所需的请求参数,而忽略其他任何输入。 这与属性绑定形成对比,后者默认会绑定所有存在匹配属性的请求参数。spring-doc.cadn.net.cn

如果既没有专门的模型对象,也无法使用构造函数绑定,而必须使用属性绑定,我们强烈建议在 allowedFields 上注册 WebDataBinder 模式(区分大小写),以防止意外设置未预期的属性。例如:spring-doc.cadn.net.cn

@Controller
public class ChangeEmailController {

	@InitBinder
	void initBinder(WebDataBinder binder) {
		binder.setAllowedFields("oldEmailAddress", "newEmailAddress");
	}

	// @RequestMapping methods, etc.

}

你还可以注册 disallowedFields 模式(不区分大小写)。然而,“允许”配置优于“禁止”配置,因为它更加明确,且不易出错。spring-doc.cadn.net.cn

默认情况下,会同时使用构造函数绑定和属性绑定。如果你想仅使用构造函数绑定,可以通过控制器内的 declarativeBinding 方法,或通过全局的 WebDataBinder,在 @InitBinder 上设置 @ControllerAdvice 标志。启用此标志可确保仅使用构造函数绑定,除非配置了 allowedFields 模式,否则不会使用属性绑定。 例如:spring-doc.cadn.net.cn

@Controller
public class MyController {

	@InitBinder
	void initBinder(WebDataBinder binder) {
		binder.setDeclarativeBinding(true);
	}

	// @RequestMapping methods, etc.

}