2

I have a problem with Spring MVC which is as follows: I use JSR 303 validation in order to make sure properties of a bean (see PasswordInfo below) are neither empty nor null.

I am also checking a business logic rule i.e. that two passwords are identical.

The issue is that if one of the string fields (currentPassword and newPassword) is empty, it is still passed to the service layer by the controller in order to check the business logic rule and of course an IllegalArgumentException is raised!

Here is the 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;
...

Here is the relevant controller method:

@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";
    }

Here is the relevant service-layer method:

@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);
    }

My concern is to avoid placing another bindingResults.hasErrors block such as:

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

...before the business logic check in order to avoid repeating myself...

Can anyone please suggest a clean solution to this problem?

4

2 回答 2

2

这里的问题是,由于您提供 aBindingResult作为参数,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;
}
于 2013-10-08T01:27:27.557 回答
0

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

于 2013-10-07T09:40:24.793 回答