说明
技术方案使用
Spring Validation
。Spring Validation
就是对Hibernate Validator
又一层封装,Hibernate Validator
对Bean校验的 JSR-303规范做了实现并扩展。结合自定义全局异常使用。
使用背景
在实际的业务开发中,例如一个表单的提交充斥大量的字段,通常前端会进行校验,但是出于安全性考虑,后端的校验也必不可少。
在常规的场景下,我们要对请求对象的所有属性做业务校验,这样的缺点是会在业务代码中充斥大量的if判断,使代码量变大并使业务代码变得异常臃肿,使用
Spring Validation
能极大的优化类似问题,让业务和基础校验松耦合。
技术实现
目前分为两种实现:
1: 自定义校验工具类(手动校验)推荐
2:接口方法类型校验
Spring Boot引入jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
自定义校验工具类
import com.haircut.mumu.common.exception.ApplicationException;
import org.springframework.util.CollectionUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import java.util.Set;
/**
* @program: ValiUtil
* @description: 校验工具类
* @author: xiatl
* @create: 2020-08-08 20:02
**/
public class ValiUtil {
/**
* 校验对象
* 返回一个错误
*
* @param t 对象
* @param <T> t
*/
public static <T> void validateBySingleError(@Valid T t, Class<?> clazz) {
Set<ConstraintViolation<@Valid T>> validateSet = Validation.buildDefaultValidatorFactory()
.getValidator().validate(t, clazz);
if (!CollectionUtils.isEmpty(validateSet)) {
for (ConstraintViolation<@Valid T> vali : validateSet) {
throw new ApplicationException(vali.getMessage());
}
}
}
/**
* 校验对象
* 返回全部错误
*
* @param t 对象
* @param <T> t
*/
public static <T> void validateByAllError(@Valid T t, Class<?> clazz) {
Set<ConstraintViolation<@Valid T>> validateSet = Validation.buildDefaultValidatorFactory()
.getValidator().validate(t, clazz);
if (!CollectionUtils.isEmpty(validateSet)) {
String messages = validateSet.stream()
.map(ConstraintViolation::getMessage)
.reduce((m1, m2) -> m1 + "\n" + m2)
.orElse("参数输入有误!");
throw new ApplicationException(messages);
}
}
}
开始使用
我们默认使用分组校验(groups)
-
ValiUtil
工具类接收两个参数:1. 要校验的Bean(BO),2. 分组标记 - BO中使用注解的形式进行校验,message为校验失败抛出的异常信息,groups为校验适用的分组。
- BO中定义了分组。
- 示例中的
save
接口校验传入了UserBO.Save.class
,意味着BO中只有groups
中标记了UserBO.Save.class
才会参与校验 - 示例中的
update
接口校验传入了UserBO.Update.class
,意味着BO中只有groups
中标记了UserBO.Update.class
才会参与校验
嵌套校验
- 通常校验存在嵌套问题,即校验对象中包含对象或集合需要校验,只需在校验对象属性上添加
@Valid
校验即可
接口方法类型校验
相对于自定义工具类自行校验的方式,该方式的校验仅支持控制器层,对于请求在
Controller
层校验比较友好嵌套校验在
自定义工具类
中已有描述。
开始使用
创建一个校验异常全局处理类
import com.zyj.crm.core.response.ResponseDataUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
/**
* @program: ValidFailExceptionHandle
* @description: 校验失败异常处理
* @author: xiatl
* @create: 2021-07-12 13:57
**/
@RestControllerAdvice
public class ValidFailExceptionHandle extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
if (ex.getBindingResult().hasErrors()) {
String msg = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
return new ResponseEntity<>(ResponseDataUtil.buildError(msg), headers, status);
}
return new ResponseEntity<>(ResponseDataUtil.buildError(ex.getMessage()), headers, status);
}
}
控制器层直接对请求对象添加 @Validated
注解,并设置分组。
常用基本Bean校验
常用的校验注解可通过源码查看获得
。。。