在 Spring 应用中的 Service 层进行验证

1、概览

本文将带你了解如何在 Spring 应用的 Service 层中使用 Spring Validation 进行校验。

2、应用分层

Spring Web 应用通常分为如下几层:

Java Web 应用的分层架构

Consumer 层或 Web 层是 Web 应用程序的最上层。它负责解析用户的输入并提供适当的响应。其他层抛出的异常也必须由 Web 层处理。由于 Web 层是应用程序的入口点,因此它负责身份认证,是防止未授权用户的第一道防线。

在 Web 层之下是 Service 层。它充当事务屏障,同时承载应用和基础设施服务。Service 层的公共 API 由应用服务提供。它们通常作为事务边界,并负责授权事务。基础设施服务提供与外部工具(包括文件系统、数据库和电子邮件服务器)连接的 “管道代码”。这些方法通常被多个应用服务使用。

Web 应用的最底层是持久层。换句话说,它负责与数据存储进行交互。

3、Service 层的验证

Service 层是应用中的一个层,用于在 Controller 和持久层之间进行通信。此外,业务逻辑也存储在 Service 层中。其中特别包括验证逻辑。Model 状态用于 Controller 层和 Service 层之间的通信。

在业务层进行验证逻辑有其优点和缺点。Spring 的验证(和数据绑定)架构并不排除任何一种方式。验证未绑定在 Web 层,这易于本地化,并且允许使用任何可用的 Validator。

此外,客户端输入数据并不总是通过 REST Controller 处理,如果不在 Service 层进行验证,非法数据可能会通过,引发多个问题。

在这种情况下,可以使用标准的 Java JSR-303 验证方案。

4、示例

使用 Spring Boot 开发一个简单的用户注册应用。

4.1、Domain

只有 nameagephonepassword 属性:

public class UserAccount {

    @NotNull(message = "Password must be between 4 to 15 characters")
    @Size(min = 4, max = 15)
    private String password;

    @NotBlank(message = "Name must not be blank")
    private String name;

    @Min(value = 18, message = "Age should not be less than 18")
    private int age;

    @NotBlank(message = "Phone must not be blank")
    private String phone;
    
    //构造函数、get、set 方法省略
}

在上述类中,使用了 @NotNull@Size@NotBlank@Min 这四个注解来确保输入属性既不是空的,也不是空白的,并且符合大小要求。

4.2、在 Service 层实现验证

有许多可用的验证解决方案。手动验证也是一个可行的选择,在将验证集成到应用的适当部分时,这提供了很大的灵活性。

在 service 类中实现验证:

@Service
public class UserAccountService {

    @Autowired
    private Validator validator;
    
    @Autowired
    private UserAccountDao dao;
    
    public String addUserAccount(UserAccount useraccount) {
        
        Set<ConstraintViolation<UserAccount>> violations = validator.validate(useraccount);

        if (!violations.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            for (ConstraintViolation<UserAccount> constraintViolation : violations) {
                sb.append(constraintViolation.getMessage());
            }
            throw new ConstraintViolationException("Error occurred: " + sb.toString(), violations);
        }

        dao.addUserAccount(useraccount);       
        return "Account for " + useraccount.getName() + " Added!";
    }
}

Validator 是 Bean Validation API 的一部分,负责验证 Java 对象。Spring 自动提供了一个 Validator 实例,可以将其注入到 UserAccountService 中。

Validator 通过 validate(...) 函数验证传递的对象。验证结果是一组 ConstraintViolation

如果没有违反验证约束(对象有效),则集合为空。否则,会抛出一个 ConstraintViolationException 异常。

4.3、实现 REST Controller

之后,构建 Spring REST Controller 类,将服务暴露给客户端,并对输入进行验证:

@RestController
public class UserAccountController {

    @Autowired
    private UserAccountService service;

    @PostMapping("/addUserAccount")
    public Object addUserAccount(@RequestBody UserAccount userAccount) {
        return service.addUserAccount(userAccount);
    }
}

在上述 REST Controller 中,没有使用 @Valid 注解来进行任何验证。

4.4、测试 Controller

启动应用,使用 Postman 或其他 API 测试工具,将如下 JSON POSTlocalhost:8080/addUserAccount 端点:

{
   "name":"Baeldung",
   "age":25,
   "phone":"1234567890",
   "password":"test",
   "useraddress":{
      "countryCode":"UK"
   }
}

测试 OK,接着尝试 POST 一个包含非法参数值的 JSON,如下:

{
   "name":"",
   "age":25,
   "phone":"1234567890",
   "password":"",
   "useraddress":{
      "countryCode":"UK"
   }
}

参数校验失败,响应如下:

Error occurred: Password must be between 4 to 15 characters, Name must not be blank

5、总结

本文介绍了 Java Web 应用的分层,以及如何在 Service 层对参数进行手动验证。

在 Service / Business 层中,这种验证方法并不局限于方法参数,还可以应用于各种对象。例如,可以从数据库加载一个对象,对其进行更改,然后在继续之前对其进行验证。


Ref:https://www.baeldung.com/spring-service-layer-validation