3

我在 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 这意味着域对象可以被注释为这样,将所有验证定义留在一个地方。

4

3 回答 3

2

最后一个选项可以通过@ModelAttribute注释来实现。

创建一个返回域对象的方法@ModelAttribute并向其添加注释。然后将相同的注释添加到要使用该对象的方法的域对象参数中。Spring 将首先从ModelAttribute方法中加载对象,然后将其与发布的数据合并。

例子:

@ModelAttribute("foobar")
public User fetchUser() {
    return loadUser();
}

   @RequestMapping(method = RequestMethod.PUT, produces = "text/html")
public String update(@ModelAttribute("foobar") @Valid User user, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
    return etc();
}
于 2012-12-03T12:45:41.373 回答
1

我正在发布另一个与我之前的答案完全无关的答案。

还有另一种解决方案:将您的域对象包装到仅公开您要验证的字段的特殊表单对象中。

例子:

public class UserForm {
    private final User user = new User();

    // User has many fields, but here we only want lastName
    @NotEmpty // Or whatever validation you want
    public String getLastName() {
        return this.user.getLastName();
    }

    public void setLastName(String lastName) {
        this.user.setLastName(lastName);
    }

    public User getUser() {
        return this.user;
    }

}
于 2012-12-03T13:30:28.980 回答
1

您可以将disabled属性用于文件中的输入标记,该jspx文件包含要标记为只读的字段的表单。

还要确保清除z与该字段相关的属性,以便 Roo 在以后对实体进行任何更改时将忽略该标记。

干杯!

于 2012-12-03T11:43:10.527 回答