在使用Spring MVC验证器执行“浅”用户输入验证后,是否有一些使用Spring MVC呈现服务层验证错误的良好实践?例如,拥有以下代码:
@Autowired
private UserService userService;
@RequestMapping(value = "user/new", method = RequestMethod.POST)
public String createNewUser(@ModelAttribute("userForm") UserForm userForm, BindingResult result, Model model){
UserFormValidator validator = new UserFormValidator(); //extending org.springframework.validation.Validator
validator.validate(userForm, result);
if(result.hasErrors()){
model.addAttribute("userForm", userForm);
return "user/new";
}
// here, for example, the user already might exist
userService.createUser(userForm.getName(), userForm.getPassword());
return "redirect:user/home";
}
虽然将此代码作为示例似乎微不足道,但在服务层验证是一项复杂的任务时,对我来说似乎是一个微妙的故事。尽管是一个荒谬的场景,UserService可能会创建一个用户列表,如果其中一个已经存在,那么视图层必须以某种方式通知其中哪些无效(例如确实存在)。
我正在寻找一个如何设计一段代码的良好实践,这使得
成为可能1)处理具有复杂数据作为输入的服务层的验证错误,并且
2)向用户提供这些验证错误
尽可能简单。有什么建议吗?
答案 0 :(得分:9)
选择通常是异常与错误代码(或响应代码),但最佳做法,至少是Bloch,只是在特殊情况下使用例外情况,这会在这种情况下取消资格,因为用户选择现有的用户名并非闻所未闻。
您的服务调用中的问题是您认为createUser
是命令性命令,没有返回值。您应该将其视为“尝试创建用户,并给我一个结果”。那么结果可能是
UserOperationResult
枚举(更好的主意,因为您可能希望在创建新用户和尝试修改用户时返回USER_ALREADY_EXISTS
)UserCreationResult
对象,它完全适合这个调用(不是一个好主意,因为你会爆炸这些)Result<T>
或UserOperationResult<T>
包装器对象,它将响应代码常量(分别为ResultCode
或UserOperationResultCode
)和返回值T
或通配符组合在一起?
当没有返回值时......只需注意不期望包装的切入点等)未经检查的例外之处在于它们避免了所有这些废话,但它们带有一系列问题。我个人坚持最后一个选择,并且过去曾经有过不错的运气。
答案 1 :(得分:5)
抛出异常/返回错误代码的替代方法是将Errors
传递给userService.createUser()
。然后可以在服务级别执行重复的用户名检查 - 并附加到Errors
的任何错误。这样可以确保所有错误(浅层和更复杂)都可以被收集起来并同时呈现给视图。
所以你可以稍微改写你的控制器方法:
@RequestMapping(value = "user/new", method = RequestMethod.POST)
public String createNewUser(@ModelAttribute("userForm") UserForm userForm, BindingResult result, Model model){
UserFormValidator validator = new UserFormValidator();
validator.validate(userForm, result);
// Pass the BindingResult (which extends Errors) to the service layer call
userService.createUser(userForm.getName(), userForm.getPassword(), result);
if(result.hasErrors()){
model.addAttribute("userForm", userForm);
return "user/new";
}
return "redirect:user/home";
}
然后您的UserServiceImpl
会检查重复的用户本身 - 例如:
public void createUser(String name, String password, Errors errors) {
// Check for a duplicate user
if (userDao.findByName(name) != null) {
errors.rejectValue("name", "error.duplicate", new String[] {name}, null);
}
// Create the user if no duplicate found
if (!errors.hasErrors()) {
userDao.createUser(name, password);
}
}
Errors
类是Spring验证框架的一部分 - 所以尽管存在对Spring的依赖,但服务层不会依赖任何与Web相关的代码。