一、场景介绍
-
@Valid
和@Validated
注解一般都直接在Controller
层对请求参数的校验而直接在入参中使用进行绑定数据校验 -
在有些情况,例如:你的参数对象中的某个字段是一个复合对象,或者业务层的某个方法所定义的入参对象也需要进行数据合法性校验,那么这种情况下如何实现像
Controller
层一样的校验效果呢? -
需要说明在这种情况下
@Validated
已经无法直接使用了,因为@Validated
注解发挥作用主要是Spring MVC
在接收参数的过程中实现了自动数据绑定校验,而在普通的业务方法或者复合参数对象中是没有办法直接绑定校验的。 -
这种情况下,我们可以通过定义一个基于
javax.validation
接口的ValidatorUtil
工具类,这样就可以在非@Validated
直接绑定校验的场景中通过校验工具类来实现对Bean
对象约束注解的校验,来校验@Valid
的规则来实现一样的校验效果
二、校验场景
-
在
Controller
层不对请求参数到校验(当然一般情况下是在Controller
进行参数绑定到时候校验参数到合法性,但是这里只是用示例说明,在Service
层也同样可以达到这种校验的效果) -
在
Service
层对实体Bean
进行@Valid
扫描到注解进行校验 -
校验自定义注解和原生注解到字段不合规性的异常
三、封装工具
-
自定义
ValidatorUtil
工具类/** * Copyright (C), 1998-2021, Shenzhen Rambo Technology Co., Ltd * 自定义对象属性规则校验工具,校验方式适配 @Valid 和 @Validated 注解的校验规则 * * @author Rambo * @date 2021/3/11 19:24 * @since 1.0.0.1 */ public class ValidatorUtil { /** 通过建造者工厂模式创建 javax.validation.Validator 对象*/ private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); /** * Bean 对象的整体校验,有不符合规范的属性,迭代异常 Message 并拼接后全部抛出 * * @author Rambo * @date 2021/3/11 19:28 * @param obj 待验证对象 * @param groups 分组 */ public static void validated(Object obj, Class<?>... groups) { Set<ConstraintViolation<Object>> resultSet = validator.validate(obj, groups); if (resultSet.size() > 0) { // 如果存在错误结果,则将其解析并进行拼凑后异常抛出 List<String> errorMessageList = resultSet.stream().map(ConstraintViolation::getMessage).collect(Collectors.toList()); StringBuilder errorMessage = new StringBuilder(); errorMessageList.forEach(o -> errorMessage.append(o).append(";")); // 抛出异常 throw new IllegalArgumentException(errorMessage.toString()); } } }
四、使用工具
-
自定义需要检验到
Order
对象@Data @ToString public class Order implements Serializable { private static final long serialVersionUID = 784930215432L; /** 订单号*/ private int id; /** 订单名称*/ @NotNull(message = "订单名称不能为空") private String orderName; /** 订单状态 定制化注解,支持参数值与指定类型数组列表值进行匹配(缺点是需要将枚举值写死在字段定义的注解中)*/ @EnumValidated(strValues = {"FAIL", "PAYED"}, message = "只能查询指定状态的订单信息-1") private String orderState; /** 订单状态枚举 定制化注解,实现参数值与枚举列表的自动匹配校验(能更好地与实际业务开发匹配)*/ @EnumValidated(enumValues = OrderStateEnum.class, message = "只能查询指定状态的订单信息-2") private String orderStateEnum; }
-
编写控制层,并且对绑定到入参不进行校验,并且模拟程序在
Service
层对@Valid
所扫描到注解进行校验@PostMapping("/util/order") @ApiOperation(value = "订单查询", notes = "订单查询,采用自定义工具类创建Validator.validate(obj, groups)来校验 @Valid 的规则") public DataResult orderList2(@RequestBody Order order) { // 采用自定义工具类创建Validator.validate(obj, groups)来校验 @Valid 的规则 ValidatorUtil.validated(order); log.info("The request is {}", order.toString()); return DataResult.success(); }
-
模拟请求参数
{ "id": 0, "orderState": "FAIL1", "orderStateEnum": "FAIL2" }
-
验证响应结果
{ "code": 10000, "msg": "只能查询指定状态的订单信息-1;只能查询指定状态的订单信息-2;订单名称不能为空;", "detail": null, "data": null }
P.S
以上
Controller
通过项目中定义@RestControllerAdvice
来进行异常统一处理,所以看到的响应结果是封装过的 -
在
Service
层对@Valid
所扫描到注解,通过自定义工具类ValidatorUtil
来进行参数的校验,在编程体验上就可以整体上保持一致