1、概览 {#1概览}
本文将带你了解如何在 Spring 应用的 Service 层中使用 Spring Validation 进行校验。
2、应用分层 {#2应用分层}
Spring Web 应用通常分为如下几层:
Consumer 层或 Web 层是 Web 应用程序的最上层。它负责解析用户的输入并提供适当的响应。其他层抛出的异常也必须由 Web 层处理。由于 Web 层是应用程序的入口点,因此它负责身份认证,是防止未授权用户的第一道防线。
在 Web 层之下是 Service 层。它充当事务屏障,同时承载应用和基础设施服务。Service 层的公共 API 由应用服务提供。它们通常作为事务边界,并负责授权事务。基础设施服务提供与外部工具(包括文件系统、数据库和电子邮件服务器)连接的 "管道代码"。这些方法通常被多个应用服务使用。
Web 应用的最底层是持久层。换句话说,它负责与数据存储进行交互。
3、Service 层的验证 {#3service-层的验证}
Service 层是应用中的一个层,用于在 Controller 和持久层之间进行通信。此外,业务逻辑也存储在 Service 层中。其中特别包括验证逻辑。Model 状态用于 Controller 层和 Service 层之间的通信。
在业务层进行验证逻辑有其优点和缺点。Spring 的验证(和数据绑定)架构并不排除任何一种方式。验证未绑定在 Web 层,这易于本地化,并且允许使用任何可用的 Validator。
此外,客户端输入数据并不总是通过 REST Controller 处理,如果不在 Service 层进行验证,非法数据可能会通过,引发多个问题。
在这种情况下,可以使用标准的 Java JSR-303 验证方案。
4、示例 {#4示例}
使用 Spring Boot 开发一个简单的用户注册应用。
4.1、Domain {#41domain}
只有 name
、age
、phone
和 password
属性:
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 层实现验证 {#42在-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 {#43实现-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 {#44测试-controller}
启动应用,使用 Postman 或其他 API 测试工具,将如下 JSON POST
到 localhost: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、总结 {#5总结}
本文介绍了 Java Web 应用的分层,以及如何在 Service 层对参数进行手动验证。
在 Service / Business 层中,这种验证方法并不局限于方法参数,还可以应用于各种对象。例如,可以从数据库加载一个对象,对其进行更改,然后在继续之前对其进行验证。
Ref:https://www.baeldung.com/spring-service-layer-validation