日常开发中,前后端都会对传递的参数进行校验,前端为的是更好的交互体验,后端则更是为了接口的安全,数据的正确性做了各种各样的校验。
环境:springboot2.3.3.RELEASE
校验依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
一、全局异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
private static Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 对于注解校验的进行统一的验证处理
* @param request
* @param e
* @return
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseBody
public R methodArgumentNotValidExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException e){
log.warn("1、数据校验不通过,请求路径:{},异常错误信息:{}", request.getRequestURI(), e);
return R.failed(getErrorMsg(e));
}
/**
* 对于注解校验的进行统一的验证处理
* @param request
* @param e
* @return
*/
@ExceptionHandler(value = BindException.class)
@ResponseBody
public R methodArgumentNotValidExceptionHandler(HttpServletRequest request, BindException e){
log.warn("2、数据校验不通过,请求路径:{},异常错误信息:{}", request.getRequestURI(), e);
return R.failed(getErrorMsg(e));
}
/**
* 默认异常处理方法
* @param request
* @param e
* @return 返回异常请求路径和异常信息
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public R defaulErrorHandler(HttpServletRequest request, Exception e){
log.error("3、异常请求路径:{},异常错误信息:{}", request.getRequestURI(), e);
return R.failed(e.getMessage());
}
/**
* 最高级别的错误拦截,防止错误信息进入到前端
* @param request
* @param e
* @return
*/
@ExceptionHandler(value = Throwable.class)
@ResponseBody
public R throwableHandler(HttpServletRequest request, Throwable e){
log.error("6、Throwable错误信息:{}, 异常请求路径:{}", request.getRequestURI(), e);
return R.failed("内部服务器错误");
}
/**
* 整理错误信息
* @param e
* @return
*/
private String getErrorMsg(Exception e){
//获取错误验证结果
StringBuilder sb = new StringBuilder();
if (e instanceof BindException){
BindException bindException = (BindException) e;
bindException.getBindingResult().getFieldErrors().forEach((error) -> {
//取出错误数据并封装
sb.append(error.getDefaultMessage());
sb.append(",");
});
} else if (e instanceof MethodArgumentNotValidException){
MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
exception.getBindingResult().getFieldErrors().forEach((error) -> {
sb.append(error.getDefaultMessage());
sb.append(",");
});
}
//去除最后一个逗号
return sb.deleteCharAt(sb.length() - 1).toString();
}
二、表单参数校验
@RestController
@RequestMapping("/args")
@Validated
public class ArgsController {
@PostMapping("test1")
public R validatedTest1(@RequestParam @Pattern(regexp = VerificationUtil.PHONE_REGEXP, message = "手机格式错误") String phone,
@RequestParam @NotEmpty(message = "名字不能为空") String name) {
return R.success(name + "的手机号是" + phone);
}
}
必须在类上添加@Validated的注解,否则普通表单类型的参数校验不生效。
触发的异常是默认异常处理3:Exception.class,如果没有3异常捕获,则触发6异常Throwable.class
三、实体类参数校验
@Data
public class ArgsDTO {
private Long id;
@NotEmpty(message = "名字不能为空!")
private String name;
@Pattern(regexp = VerificationUtil.PHONE_REGEXP, message = "手机号格式错误!")
private String phone;
@Email(message = "邮箱格式错误!")
private String email;
}
@PostMapping("test2")
public R validatedTest2(@Validated @RequestBody ArgsDTO argsDTO) {
return R.success(argsDTO);
}
触发的异常是异常1:MethodArgumentNotValidException.class
单单在类上面添加@Validated不生效,需要在实体参数前添加@Validated才生效。类上不添加也可以。
四、对象嵌套
@Data
public class ArgsListDTO {
private String code;
@NotEmpty(message = "参数成员不能为空")
@Valid
private List<ArgsDTO> argsDTOList;
}
@PostMapping("test3")
public R validatedTest3(@Validated @RequestBody ArgsListDTO argsListDTO) {
return R.success(argsListDTO);
}
需要在嵌套对象添加@Valid才能使嵌套对象里面的属性校验生效
五、分组校验
根据不同的业务场景对同一个属性执行不同的校验规则。比如主键id,添加的时候可以为空,更新的时候不能为空。
public interface AddGroup extends Default {
}
public interface UpdGroup extends Default {
}
@Data
public class ArgsDTO {
@NotNull(message = "更新id不能为空", groups = UpdGroup.class)
private Long id;
@NotEmpty(message = "名字不能为空!", groups = {AddGroup.class, UpdGroup.class})
private String name;
@Pattern(regexp = VerificationUtil.PHONE_REGEXP, message = "手机号格式错误!")
private String phone;
@Email(message = "邮箱格式错误!")
private String email;
}
@PostMapping("test4")
public R validatedTest42(@Validated @RequestBody ArgsDTO argsDTO) {
return R.success(argsDTO);
}
@PostMapping("test5")
public R validatedTest5(@Validated(value = {AddGroup.class, UpdGroup.class}) @RequestBody ArgsDTO argsDTO) {
return R.success(argsDTO);
}
分组AddGroup、UpdGroup必须继承默认默认校验分组,不然实体中没有设置分组的属性不会被校验。比如ArgsDTO中的phone和email字段。