【问题标题】:Validation issue in a Spring MVC controller using JSR 303 in order to deal with empty values使用 JSR 303 处理空值的 Spring MVC 控制器中的验证问题
【发布时间】:2013-10-05 14:32:01
【问题描述】:

我对 Spring MVC 有如下问题:我使用 JSR 303 验证来确保 bean 的属性(请参阅下面的 PasswordInfo)既不为空也不为 null

也在检查业务逻辑规则,即两个密码是否相同

问题在于,如果字符串字段(currentPassword 和 newPassword)之一为空,控制器仍会将其传递给服务层以检查业务逻辑规则,当然会引发 IllegalArgumentException !

这是PasswordInfo bean:

@RooEquals
@RooJavaBean
public class PasswordInfo {

    @NotNull(groups = { ValidationGroups.PasswordModification.class })
    @NotEmpty(groups = { ValidationGroups.PasswordModification.class })
    private String currentPassword;

    @NotNull(groups = { ValidationGroups.PasswordModification.class, ValidationGroups.PasswordReset.class })
    @NotEmpty(groups = { ValidationGroups.PasswordModification.class, ValidationGroups.PasswordReset.class })       
    @Size(min = 6, groups = { ValidationGroups.PasswordModification.class, ValidationGroups.PasswordReset.class })
    private String newPassword;
...

这里是相关的控制器方法:

@RequestMapping(value = "/preference/password", method = RequestMethod.POST, produces = "text/html")
    public String modifyPassword(@ModelAttribute @Validated({ ValidationGroups.PasswordModification.class }) PasswordInfo passwordInfo,
            BindingResult bindingResult, Model model, @CurrentMember Member member) {
        if (!preferenceService.passwordsMatch(member.getPassword(), passwordInfo.getCurrentPassword())) {
            bindingResult.rejectValue("currentPassword", "controller.preference.wrong_current_password");
        }

        if (bindingResult.hasErrors()) {
            model.addAttribute("passwordInfo", passwordInfo);
            return "preference/password";
        }

        preferenceService.modifyPassword(member, passwordInfo.getNewPassword());
        return "redirect:/preference/password";
    }

下面是相关的服务层方法:

@Override
    public boolean passwordsMatch(String encrypted, String plain) {
        if (encrypted == null || encrypted.isEmpty() || plain == null || plain.isEmpty()) {
            throw new IllegalArgumentException("One argument is null or empty");
        }
        return passwordEncoder.matches(plain, encrypted);
    }

我担心的是避免放置另一个 bindingResults.hasErrors 块,例如:

if (bindingResult.hasErrors()) {
        model.addAttribute("passwordInfo", passwordInfo);
        return "preference/password";
    }

...在业务逻辑检查之前为了避免重复自己...

谁能提出一个干净的解决方案来解决这个问题?

【问题讨论】:

    标签: spring validation spring-mvc dry bean-validation


    【解决方案1】:

    这里的问题是,由于您提供了BindingResult 作为参数,Spring MVC 期望您在控制器方法中处理您的验证问题。它不会直接拒绝请求(即阻止调用您的控制器方法并引发异常。)您可能只需要重组您的方法以获得所需的结果:

    @RequestMapping(value = "/preference/password", method = RequestMethod.POST, produces = "text/html")
    public String modifyPassword(@ModelAttribute @Validated({ ValidationGroups.PasswordModification.class }) PasswordInfo passwordInfo,
                                 BindingResult bindingResult, Model model, @CurrentMember Member member) {
        String view = "preference/password";
        if (!bindingResult.hasErrors()) {
            if (preferenceService.passwordsMatch(member.getPassword(), passwordInfo.getCurrentPassword())) {
                preferenceService.modifyPassword(member, passwordInfo.getNewPassword());
                view = "redirect:/preference/password";
            } else {
                bindingResult.rejectValue("currentPassword", "controller.preference.wrong_current_password");
            }
        }
        // NOTE: I have removed the call to model.addAttribute("passwordInfo", passwordInfo) because it should already exist in the model, no?
        return view;
    }
    

    【讨论】:

    • 谢谢。您提供的代码实际上是一个非常好的主意。我真的很想知道 Spring MVC 的最佳(和既定)实践是什么:使用类似于您的代码(效果很好,但阅读和测试稍微困难一些 - 嵌套 ifs)或继续放松服务层检查:保留“null”检查但删除“isEmpty”检查...
    • 您对我最后的评论有何看法?
    • 我认为没有真正的“最佳”或“既定”方式来构建您的逻辑......至少从 Spring MVC 的角度来看不是。当然,我会保留您的验证,因为这就是它的用途:验证。
    • 另外,关于我给出的示例,我仍然有使用单进单出范式的习惯。在我看来,它实际上创建了更简洁的代码,但这在很大程度上取决于个人喜好(或您的团队的喜好)。如果您对该主题感兴趣,这里有一个讨论:stackoverflow.com/questions/4838828/…
    • 很抱歉继续评论,但您可能还对 Spring 支持自定义验证器将您的 passwordsMatch() 方法移动到它自己的 303 验证器感兴趣:docs.spring.io/spring/docs/current/spring-framework-reference/… =)
    【解决方案2】:

    我没有遵循您的整个示例,老实说,我不完全理解您的问题,但我想知道的一件事是您为什么不使用 Bean Validation 以及密码匹配约束?您可以为 PasswordInfo 编写自定义类约束。

    【讨论】:

    • 哈代你好!实际上,这个问题与 Spring MVC 的关系比与 JSR 303 的关系更大——尽管我确实在我的帖子中添加了“bean-validation”标签。基本上,当我将自定义验证与 JSR 303 验证混合时,我正在寻找一种干净的方法来避免重复 if (bindingResult.hasErrors()) 块。你明白了吗?
    猜你喜欢
    • 1970-01-01
    • 2011-05-10
    • 1970-01-01
    • 1970-01-01
    • 2011-07-24
    • 2017-06-02
    • 2018-08-15
    • 1970-01-01
    • 2018-01-30
    相关资源
    最近更新 更多