【问题标题】:Constraints check controller layer vs service layer约束检查控制器层与服务层
【发布时间】:2019-02-06 05:15:56
【问题描述】:

我正在尝试学习 spring 并实现这一目标,我正在从头开始构建一个 REST 应用程序。我很困惑我应该在哪里检查应用程序中的约束:控制器层与服务层。 例如,在创建用户方法中,我想检查是否有任何其他用户使用相同的电子邮件,因为电子邮件在我的数据库中是唯一的。我还想检查密码是否匹配(密码和“确认密码”字段)等。

目前,在我的实现中,所有这些都在控制器层进行了验证,因此我可以为每种方法返回一个 ResponseEntity。

    @PostMapping("/signUp")
    public ResponseEntity<Object> createUser(@RequestBody RegisterUserDto user) {

        if (userService.getUserByEmail(user.getEmailAddress()) != null) {

            return ResponseEntity.badRequest().body("email already exists");
        }

        if (!user.getPassword().equals(user.getConfirmPassword())) {

            return ResponseEntity.badRequest().body("passwords are not the same");
        }

        User savedUser = null;

        try {

            savedUser = userService.createUser(userDtoConversions.convertToEntityRegister(user));

        } catch (ParseException e) {

            e.printStackTrace();
        }

        URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
                .buildAndExpand(savedUser.getId()).toUri();

        return ResponseEntity.created(location).build();
    }

在服务层创建用户方法:

    @Override
    @Transactional
    public User createUser(User newUser) {

        newUser.setDateCreated(new Date());

        return userRepository.save(newUser);
    }

那么哪种方法更好呢?如果我在服务层检查约束和验证,我应该返回什么以便在我的控制器中知道为什么创建用户失败?

【问题讨论】:

  • 将其视为漏斗的一种有用方法:我可以设想多个可能想要更新该记录的接口吗? (JSON API 和 HTML 接口是两个很好的例子。)如果是这样,那么该功能将进入两个接口都使用的公共服务中,从而减少了重复。 (在您的特定情况下,您可能只需要在数据库列上使用不区分大小写的唯一约束。您可能还是想查询它。最后,如果可能,请使用 Instant 而不是 Date。)跨度>

标签: java spring rest model-view-controller


【解决方案1】:

在我看来,处理异常的最佳位置是服务层。对于我来说,REST 控制器方法最多应该处理请求并将其传递给服务方法。

使用这种方法,您可以定义非常明确的层,这些层可以完成非常明确的工作。例如,您的服务层将处理请求的验证、持久化操作,并且还将(如果需要)向控制器提供返回对象,然后将其包装到适当的响应对象中(在您的情况下为ResponseEntity)。

考虑到这一点,没有什么能阻止您在服务层抛出任何类型的异常并转化为正确的响应。 Spring 有一个非常简洁和强大的机制,可以精确地完成所谓的异常处理程序。

因此,对于您的密码检查操作,您可以执行以下操作:

if (!user.getPassword().equals(user.getConfirmPassword())) {
    throw new PasswordMismatchException("Passwords are not the same for user:: " + user.getName());
}

PasswordMismatchException 是 RuntimeException。有了类似的东西,您可以继续设置一个 ExceptionHandler 以及适当的方法来拦截它并将其转换为响应。一个简单的例子是:

@RestControllerAdvice
public class ApplicationExceptionHandler {

   @ExceptionHandler(PasswordMismatchException.class)
   public ResponseEntity<String> handleBadPasswords(PasswordMismatchException e) {
       return ResponseEntity.badRequest().body(e.getMessage());
   }

}

您可以在 Spring 的文档中阅读更多信息:

Spring ExceptionHandler

Exception Handling in Spring

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-06-17
    • 2013-03-19
    • 1970-01-01
    • 2012-01-10
    • 2017-08-08
    • 2017-10-19
    • 2023-02-22
    相关资源
    最近更新 更多