DataBinder

@Controller@ControllerAdvice 类可以具有@InitBinder 方法来初始化WebDataBinder 实例,这些实例反过来可以

  • 将请求参数绑定到模型对象。

  • 将请求值从字符串转换为对象属性类型。

  • 在渲染 HTML 表单时将模型对象属性格式化为字符串。

@Controller 中,DataBinder 定制适用于控制器内的局部范围,甚至适用于通过注解按名称引用的特定模型属性。在@ControllerAdvice 中,定制可以应用于所有控制器或一部分控制器。

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

  • Java

  • Kotlin

@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 注解。

或者,当通过共享的FormattingConversionService 使用基于Formatter 的设置时,您可以重复使用相同的方法并注册控制器特定的Formatter 实例,如下例所示

  • Java

  • Kotlin

@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 请求,涉及将请求参数绑定到模型对象。默认情况下,请求参数可以绑定到模型对象的任何公共属性,这意味着恶意客户端可以为模型对象图中存在的但预期不会设置的属性提供额外的值。这就是为什么模型对象设计需要仔细考虑的原因。

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

一个好的做法是使用专用模型对象,而不是将您的域模型(例如 JPA 或 Hibernate 实体)用于 Web 数据绑定。例如,在一个更改电子邮件地址的表单上,创建一个ChangeEmailForm 模型对象,该对象只声明输入所需的属性

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;
	}

}

另一个好的做法是应用构造函数绑定,它只使用它需要的请求参数作为构造函数参数,任何其他输入都将被忽略。这与属性绑定形成对比,属性绑定默认情况下会绑定每个与属性匹配的请求参数。

如果专用模型对象或构造函数绑定都不够,并且您必须使用属性绑定,我们强烈建议在WebDataBinder 上注册allowedFields 模式(区分大小写),以防止设置意外属性。例如

@Controller
public class ChangeEmailController {

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

	// @RequestMapping methods, etc.

}

您还可以注册disallowedFields 模式(不区分大小写)。但是,"允许"配置比"不允许"配置更可取,因为它更明确且不易出错。

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

@Controller
public class MyController {

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

	// @RequestMapping methods, etc.

}