@InitBinder

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

@Controller or @ControllerAdvice classes can have @InitBinder methods to initialize WebDataBinder instances that in turn can:

  • Bind request parameters to a model object.

  • Convert request values from string to object property types.

  • Format model object properties as strings when rendering HTML forms.

@Controller 中,DataBinder 自定义在控制器中局部应用,甚至应用于通过注释按名称引用的特定模型属性。在 @ControllerAdvice 中,自定义可以应用于全部或部分控制器。

In an @Controller, DataBinder customizations apply locally within the controller, or even to a specific model attribute referenced by name through the annotation. In an @ControllerAdvice customizations can apply to all or a subset of controllers.

你可以在 DataBinder 中注册 PropertyEditorConverterFormatter 组件用于类型转换。或者,你可以在一个全局共享的 FormattingConversionService 中使用 MVC config 注册 ConverterFormatter 组件。

You can register PropertyEditor, Converter, and Formatter components in the DataBinder for type conversion. Alternatively, you can use the MVC config to register Converter and Formatter components in a globally shared FormattingConversionService.

@InitBinder 方法可以具有许多与 @RequestMapping 方法相同的参数,但一个值得注意的例外是 @ModelAttribute。此类方法通常具有一个 WebDataBinder 参数(用于注册)和一个 void 返回值,例如:

@InitBinder methods can have many of the same arguments that @RequestMapping methods have, with the notable exception of @ModelAttribute. Typically, such methods have a WebDataBinder argument (for registrations) and a void return value, for example:

Java
@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 Defining an @InitBinder method.
Kotlin
@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))
	}

	// ...
}
2 Defining an @InitBinder method.

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

Alternatively, when you use a Formatter-based setup through a shared FormattingConversionService, you can re-use the same approach and register controller-specific Formatter implementations, as the following example shows:

Java
@Controller
public class FormController {

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

	// ...
}
1 Defining an @InitBinder method on a custom formatter.
Kotlin
@Controller
class FormController {

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

	// ...
}
2 Defining an @InitBinder method on a custom formatter.

Model Design

适用于 Web 请求的 Data binding 涉及将请求参数绑定到一个模型对象。默认情况下,请求参数可以绑定到模型对象的任何公共属性,这意味着恶意客户端可以为模型对象图中存在的属性(但不是预期设置的属性)提供额外的值。这就是为什么模型对象设计需要仔细考虑的原因。

Data binding for web requests involves binding request parameters to a model object. By default, request parameters can be bound to any public property of the model object, which means malicious clients can provide extra values for properties that exist in the model object graph, but are not expected to be set. This is why model object design requires careful consideration.

模型对象及其嵌套对象的图形有时也称为 command objectform-backing objectPOJO(普通旧 Java 对象)。

The model object, and its nested object graph is also sometimes referred to as a command object, form-backing object, or POJO (Plain Old Java Object).

一个好做法是使用 专用模型对象,而不是公开你的域模型,例如用于web数据绑定的JPA或Hibernate实体。例如,在更改电子邮件地址的表单上,创建一个仅声明输入所需属性的`ChangeEmailForm`模型对象:

A good practice is to use a dedicated model object rather than exposing your domain model such as JPA or Hibernate entities for web data binding. For example, on a form to change an email address, create a ChangeEmailForm model object that declares only the properties required for the input:

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

}

另一个好习惯是应用 constructor binding,它只使用构造函数参数所需的请求参数,而忽略其他任何输入。这与属性绑定形成对比,后者默认绑定有匹配属性的每个请求参数。

Another good practice is to apply constructor binding, which uses only the request parameters it needs for constructor arguments, and any other input is ignored. This is in contrast to property binding which by default binds every request parameter for which there is a matching property.

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

If neither a dedicated model object nor constructor binding is sufficient, and you must use property binding, we strongy recommend registering allowedFields patterns (case sensitive) on WebDataBinder in order to prevent unexpected properties from being set. For example:

@Controller
public class ChangeEmailController {

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

	// @RequestMapping methods, etc.

}

您还可以注册 disallowedFields 模式(不区分大小写)。但是,“allowed”配置要优于“disallowed”配置,因为它更加明确而且不易出错。

You can also register disallowedFields patterns (case insensitive). However, "allowed" configuration is preferred over "disallowed" as it is more explicit and less prone to mistakes.

默认情况下,同时使用构造函数和属性绑定。如果您只想使用构造函数绑定,可以在控制器中本地或全局地通过 @ControllerAdviceWebDataBinder 上设置 declarativeBinding 标志。打开此标志可确保仅使用构造函数绑定,并且在未配置 allowedFields 模式的情况下不使用属性绑定。例如:

By default, constructor and property binding are both used. If you want to use constructor binding only, you can set the declarativeBinding flag on WebDataBinder through an @InitBinder method either locally within a controller or globally through an @ControllerAdvice. Turning this flag on ensures that only constructor binding is used and that property binding is not used unless allowedFields patterns are configured. For example:

@Controller
public class MyController {

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

	// @RequestMapping methods, etc.

}