我在 spring-mvc 中似乎是一个常见问题。我的几个域对象具有不可更新的字段,因此在我看来我没有绑定这些字段。 出于竞争考虑,将这些从视图中排除的方法是编辑 spring-roo 脚手架视图,将参数上的渲染属性设置为 false。
由于 spring-mvc 创建对象的新实例而不是更新现有对象,因此这些字段为空。然而,这意味着对象在控件到达控制器之前验证失败。
我的许多实体将具有在视图中不可更新的额外字段,因此我希望能够提出一个通用的解决方案,而不是一遍又一遍地不断地做同样的工作(违反 DRY)。
如果视图中省略了字段,如何允许以一致的方式进行验证?
@RequestMapping(method = RequestMethod.PUT, produces = "text/html")
public String UserController.update(@Valid User user, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
if (bindingResult.hasErrors()) {
populateEditForm(uiModel, user);
return "admin/users/update";
}
uiModel.asMap().clear();
user.merge();
return "redirect:/admin/users/" + encodeUrlPathSegment(user.getId().toString(), httpServletRequest);
}
可能的解决方案:
从控制器中省略 @Valid 注释。
优点
- 易于实施。
- 容易理解。
缺点
- 意味着为每个对象的每次更新更改控制器方法。
- 验证与应用程序的所有其余部分不同。
- 没有简单的方法将绑定错误返回给视图(之后需要验证对象)
为需要省略字段的方法添加自定义验证器
例子:
@InitBinder
public void initBinder(WebDataBinder binder, HttpServletRequest request) {
if (request.getMethod().equals("PUT")) {
binder.setDisallowedFields("registrationDate", "password");
Validator validator = binder.getValidator();
Validator userUpdateValidator = new UserUpdateValidator();
binder.setValidator(userUpdateValidator);
}
}
优点
- 清流。
缺点
- 深受 DRY 问题的困扰。这意味着如果域对象以任何方式更改,我需要重新验证。
- 保存时的字段验证与 Hibernate 验证不同。
- 省略验证和手动验证没有明显的好处。
会考虑如果?
- 自定义验证器可以委托给标准 JSR-303 验证器,但只是省略字段。
从域对象中删除 JSR-303 注释
不是一个选项,这意味着在保存之前没有对对象进行验证。更糟糕的是,我相信它会影响为数据库生成的 DDL,从而消除数据库本身的约束。为了完整起见,只放在这里
在验证发生之前查找域对象
这个解决方案的想法是在更新之前查找现有的域对象。从请求中将任何非空字段复制到旧对象。 优点 - 验证可以通过正常周期。- 验证不需要根据您所暗示的方法而改变。
缺点
- 访问控制器之前的数据库访问有点异味。
- 我看不到任何实现这一点的方法。
- 不适用于在对象生命周期的其他阶段需要省略的字段。例如,如果在创建期间添加时间戳。
我想知道如何实现一个委托给标准 JSR-303 验证器的验证器,或者如何在修改它之前查找对象。或者如果有人有任何其他可能的解决方案?
这些解决方案中的任何一个都允许处理在多个对象上保持一致。
希望两者都允许添加注释,例如。@RooCreateOnly 这意味着域对象可以被注释为这样,将所有验证定义留在一个地方。