@InitBinder

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

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

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

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

@Controller 中,DataBinder 自定义应用于控制器内的局部,或者甚至通过注解引用名称来应用于特定的模型属性。 在 @ControllerAdvice 中,自定义可以应用于所有或控制器的一个子集。

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

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

@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 方法。

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

@Controller
public class FormController {

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

  // ...
}

Model 设计

WEB 请求的 数据绑定 涉及将请求参数绑定到模型对象。 默认情况下,请求参数可以绑定到模型对象的任何公共属性,这意味着恶意客户端可以为模型对象图中存在的属性提供额外的值, 但这些属性并不期望被设置。这就是为什么模型对象设计需要仔细考虑。

模型对象及其嵌套对象图有时也被称为 命令对象表单支持对象POJO(Plain Old Java Object,普通旧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 methods, etc.

}

您也可以注册 disallowedFields 模式(不区分大小写)。然而,"允许" 配置比 "不允许" 配置更受青睐,因为它更明确,也更不容易出错。

默认情况下,构造函数绑定和属性绑定都被使用。如果您只想使用构造函数绑定,您可以通过 @InitBinder 方法在 WebDataBinder 上设置 declarativeBinding 标志,这可以局部地在控制器内进行,也可以通过 @ControllerAdvice 全局进行。打开此标志确保只使用构造函数绑定,并且除非配置了 allowedFields 模式,否则不使用属性绑定。

例如:

@Controller
public class MyController {

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

  // @RequestMapping methods, etc.

}