@InitBinder
@Controller 或 @ControllerAdvice 个类可以有 @InitBinder 个方法来初始化 WebDataBinder 个实例,这些实例反过来可以:
-
将请求参数绑定到模型对象。
-
将请求值从字符串转换为对象属性类型。
-
在渲染HTML表单时,将模型对象属性格式化为字符串。
在 @Controller 中,DataBinder 的自定义仅在控制器内局部生效,
甚至可以通过注解按名称引用特定的模型属性。在 @ControllerAdvice 中,自定义可以应用于所有控制器或部分控制器。
您可以在 DataBinder 中注册 PropertyEditor、Converter 和 Formatter 组件以进行类型转换。或者,您可以使用
MVC 配置 在全局共享的 FormattingConversionService 中注册 Converter 和 Formatter 组件。
@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方法。 |
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
-
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(Plain Old Java Object)。 |
一个良好的实践是使用专用的模型对象,而不是将你的领域模型(如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 个模式(不区分大小写)。但是,与“禁止”配置相比,更推荐使用“允许”配置,因为它更明确且不易出错。
默认情况下,构造函数绑定和属性绑定都会被使用。如果只想使用构造函数绑定,可以通过在控制器内局部地或通过一个@ControllerAdvice全局地调用@InitBinder方法,将declarativeBinding标志设置在WebDataBinder上。启用此标志可确保仅使用构造函数绑定,除非配置了allowedFields模式,否则不会使用属性绑定。例如:
@Controller
public class MyController {
@InitBinder
void initBinder(WebDataBinder binder) {
binder.setDeclarativeBinding(true);
}
// @RequestMapping methods, etc.
}