使用JSR303处理空值的SpringMVC控制器中的验证问题
SpringMVC有如下问题:我使用JSR303验证来确保bean的属性(参见下面的PasswordInfo)既不是空的也不是空的 我还检查了一条业务逻辑规则,即两个密码相同 问题是,如果其中一个字符串字段(currentPassword和newPassword)为空,它仍然由控制器传递到服务层,以检查业务逻辑规则,当然会引发IllegalArgumentException强> 下面是使用JSR303处理空值的SpringMVC控制器中的验证问题,spring,validation,spring-mvc,dry,bean-validation,Spring,Validation,Spring Mvc,Dry,Bean Validation,SpringMVC有如下问题:我使用JSR303验证来确保bean的属性(参见下面的PasswordInfo)既不是空的也不是空的 我还检查了一条业务逻辑规则,即两个密码相同 问题是,如果其中一个字符串字段(currentPassword和newPassword)为空,它仍然由控制器传递到服务层,以检查业务逻辑规则,当然会引发IllegalArgumentException 下面是PasswordInfobean: @RooEquals @RooJavaBean public class Pas
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);
}
以下是相关的服务层方法:
@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";
}
…在业务逻辑检查之前,以避免重复我自己的操作
有人能为这个问题提供一个干净的解决方案吗?我并没有遵循您的全部示例,老实说,我并不完全理解您的问题,但我想知道的一件事是,为什么您不将Bean验证也用于密码匹配约束?您可以为PasswordInfo编写自定义类约束。这里的问题是,由于您提供了一个
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;
}
哈代你好!事实上,这个问题与SpringMVC的关系比与JSR303的关系更大——尽管我在我的文章中添加了“BeanValidation”标记。基本上,我正在寻找一种干净的方法来避免在混合使用自定义验证和JSR303验证时重复
if(bindingResult.hasErrors())
块。你明白重点了吗?谢谢。实际上,您提供的代码是一个非常好的主意。我真的很想知道SpringMVC的最佳实践是什么:使用与您类似的代码(它工作得很好,但对嵌套ifs的读取和测试稍微有点困难),或者继续并放松服务层检查:保留“null”检查,但删除“isEmpty”检查…你对我的最后一条评论有什么看法?我不认为真的有一种“最佳”或“既定”的方式来构造你的逻辑…至少从Spring MVC的角度来看不是这样。我肯定会保留你的验证,因为这就是它的目的:验证。另外,关于我给出的例子,我仍然有使用单输入单输出范例的习惯。在我看来,它实际上创建了更干净的代码,但这在很大程度上取决于个人偏好(或团队偏好)。如果您对这个主题感兴趣,这里有一个关于它的讨论:很抱歉继续评论,但您可能还对Spring支持自定义验证程序移动密码匹配()感兴趣方法到其自己的303验证程序:=)