DataBinder
DataBinder
@Controller
或 @ControllerAdvice
类可以有 @InitBinder
方法来初始化 WebDataBinder
实例,这些实例可以:
-
将请求参数绑定到模型对象。
-
将请求值从字符串转换为对象属性类型。
-
在渲染 HTML 表单时将模型对象属性格式化为字符串。
在 @Controller
中,DataBinder
的自定义设置仅适用于控制器内部,或者适用于通过注解按名称引用的特定模型属性。在 @ControllerAdvice
中,自定义设置可以适用于所有控制器或其子集。
您可以在 DataBinder
中注册 PropertyEditor
、Converter
和 Formatter
组件以进行类型转换。或者,您可以使用 WebFlux 配置 在全局共享的 FormattingConversionService
中注册 Converter
和 Formatter
组件。
- 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));
}
// ...
}
使用
@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))
}
// ...
}
使用
@InitBinder
注解。
或者,当通过共享的 FormattingConversionService
使用基于 Formatter
的设置时,您可以重用相同的方法并注册特定于控制器的 Formatter
实例,如下例所示:
- Java
- Kotlin
@Controller
public class FormController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); 1
}
// ...
}
添加自定义格式化器(在这种情况下是
DateFormatter
)。
@Controller
class FormController {
@InitBinder
fun initBinder(binder: WebDataBinder) {
binder.addCustomFormatter(DateFormatter("yyyy-MM-dd")) 1
}
// ...
}
添加自定义格式化器(在这种情况下是
DateFormatter
)。
模型设计
数据绑定 用于 web 请求,涉及将请求参数绑定到模型对象。默认情况下,请求参数可以绑定到模型对象的任何公共属性,这意味着恶意客户端可以提供额外的值给模型对象图中存在但不应被设置的属性。这就是为什么模型对象设计需要仔细考虑的原因。
模型对象及其嵌套对象图有时也被称为 命令对象、表单支持对象 或 POJO(普通旧 Java 对象)。
一个好的实践是使用 专用模型对象,而不是暴露你的领域模型,例如 JPA 或 Hibernate 实体,以进行网络数据绑定。例如,在一个更改电子邮件地址的表单上,创建一个 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
模式(不区分大小写)。然而,“allowed” 配置优于 “disallowed”,因为它更明确且不易出错。
默认情况下,构造函数绑定和属性绑定都被使用。如果你只想使用构造函数绑定,可以通过 @InitBinder
方法在控制器内部或通过 @ControllerAdvice
全局设置 WebDataBinder
的 declarativeBinding
标志。开启此标志确保只使用构造函数绑定,除非配置了 allowedFields
模式,否则不使用属性绑定。例如:
@Controller
public class MyController {
@InitBinder
void initBinder(WebDataBinder binder) {
binder.setDeclarativeBinding(true);
}
// @RequestMapping methods, etc.
}