DataBinder
@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
中注册 PropertyEditor
、Converter`和 `Formatter
组件进行类型转换。或者,您可以使用 WebFlux config 在全局共享 FormattingConversionService`中注册 `Converter
和 Formatter
组件。
You can register PropertyEditor
, Converter
, and Formatter
components in the
DataBinder
for type conversion. Alternatively, you can use the
WebFlux config to register
Converter
and Formatter
components in a globally shared FormattingConversionService
.
- 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 | Using the @InitBinder annotation.
|
2 | Using the @InitBinder annotation. |
另外,当使用通过共享的`FormattingConversionService` 形成的基于 Formatter
的设置时,您可以重新使用相同的方法并注册特定于控制器的 Formatter
实例,如下面的示例所示:
Alternatively, when using a Formatter
-based setup through a shared
FormattingConversionService
, you could re-use the same approach and register
controller-specific Formatter
instances, as the following example shows:
- Java
-
@Controller public class FormController { @InitBinder protected void initBinder(WebDataBinder binder) { binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); 1 } // ... }
1 | Adding a custom formatter (a DateFormatter , in this case).
|
2 | Adding a custom formatter (a DateFormatter , in this case). |
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 object、form-backing object 或 POJO(普通旧 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.
默认情况下,同时使用构造函数和属性绑定。如果您只想使用构造函数绑定,可以在控制器中本地或全局地通过 @ControllerAdvice
在 WebDataBinder
上设置 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.
}