使用Spring MVC向用户呈现服务级别验证错误的良好实践

Ale*_*eld 8 java validation spring spring-mvc

在使用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";
} 
Run Code Online (Sandbox Code Playgroud)

虽然将此代码作为示例似乎微不足道,但在服务层验证是一项复杂的任务时,对我来说似乎是一个微妙的故事.尽管是一个荒谬的场景,UserService可能会创建一个用户列表,如果其中一个已经存在,那么必须以某种方式通知视图层哪些用户无效(例如已经存在).

我正在寻找一个如何设计一段代码的良好实践,这使得它成为可能

1)处理具有复杂数据作为输入的服务层的验证错误,和

2)向用户呈现这些验证错误

尽可能简单.有什么建议?

Eme*_*gia 9

选择通常是异常与错误代码(或响应代码),但最佳实践,至少是Bloch,只是在特殊情况下使用异常,这在这种情况下会使他们失去资格,因为用户选择的现有用户名不是闻所未闻.

服务调用中的问题是您假设createUser是一个没有返回值的命令式命令.您应该将其视为"尝试创建用户,并给我一个结果".那么结果可能就是

  • 整数代码(可怕的想法)
  • 来自共享结果枚举的常量(由于可维护性,仍然是个坏主意)
  • 来自UserOperationResult枚举的更具体的常量(更好的主意,因为您可能希望USER_ALREADY_EXISTS在创建新用户时和尝试修改用户时返回)
  • 一个UserCreationResult对这个调用完全自定义的对象(不是一个好主意,因为你会爆炸这些)
  • 一个Result<T>UserOperationResult<T>包装器对象,它组合了一个响应代码常量(ResultCodeUserOperationResultCode分别)和一个返回值T,或者?没有返回值时的通配符......只需要注意不期望包装器的切入点等

未经检查的例外之处在于它们避免了所有这些废话,但它们带有一系列问题.我个人坚持最后一个选项,并且过去曾经有过不错的运气.


Wil*_*ing 5

抛出异常/返回错误代码的替代方法是将to传递ErrorsuserService.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";
} 
Run Code Online (Sandbox Code Playgroud)

然后你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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Errors班是Spring的验证框架的一部分-所以虽然会有Spring的依赖关系,服务层不会有任何网络相关的代码的任何依赖.