@InitBinder

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

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

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

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

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

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

@InitBinder方法可以具有许多与@RequestMapping方法相同的参数,但与@ModelAttribute的显著异常。通常,这些方法具有一个WebDataBinder参数(用于注册)和一个void返回值,例如:

  • 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 (1)
	protected void initBinder(WebDataBinder binder) {
		binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
	}

	// ...
}
1 在自定义格式化程序上定义@InitBinder方法。
@Controller
class FormController {

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

	// ...
}
1 在自定义格式化程序上定义@InitBinder方法。

模型设计

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方法等

}
disallowedFields模式(不区分大小写)。但是,“允许”配置优于“不允许”配置,因为它更明确,更不容易出错。

@InitBinder方法中在控制器内部或通过 @ControllerAdvice全局设置 WebDataBinder上的 declarativeBinding标志来实现。打开此标志可以确保仅使用构造函数绑定,并且除非配置了 allowedFields模式,否则不使用属性绑定。例如:

@Controller
public class MyController {

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

	// @RequestMapping方法等

}