SpringBoot Validation优雅的全局参数校验

前言

我们都知道在平时写controller时候,都需要对请求参数进行后端校验,一般我们可能会这样写

1public String add(UserVO userVO) { 2 if(userVO.getAge() == null){ 3 return "年龄不能为空"; 4 } 5 if(userVO.getAge() > 120){ 6 return "年龄不能超过120"; 7 } 8 if(userVO.getName().isEmpty()){ 9 return "用户名不能为空"; 10 } 11 // 省略一堆参数校验... 12 return "OK"; 13}

业务代码还没开始写呢,光参数校验就写了一堆判断。这样写虽然没什么错,但是给人的感觉就是:不优雅,不专业,代码可读性也很差,一看就是新手写的代码

作为久经战争的老司机怎么能这样呢,大神是不允许这样代码出现的,其实SpringBoot提供整合了参数校验解决方案spring-boot-starter-validation

整合使用

在SpringBootv2.3之前的版本只需要引入 web 依赖就可以了他包含了validation校验包在此之后SpringBoot版本就独立出来了需要单独引入依赖

1<!--参数校验--> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-starter-validation</artifactId> 5 </dependency>

内置的校验注解有很多,罗列如下:

注解校验功能
@AssertFalse必须是false
@AssertTrue必须是true
@DecimalMax小于等于给定的值
@DecimalMin大于等于给定的值
@Digits可设定最大整数位数和最大小数位数
@Email校验是否符合Email格式
@Future必须是将来的时间
@FutureOrPresent当前或将来时间
@Max最大值
@Min最小值
@Negative负数(不包括0)
@NegativeOrZero负数或0
@NotBlank不为null并且包含至少一个非空白字符
@NotEmpty不为null并且不为空
@NotNull不为null
@Null为null
@Past必须是过去的时间
@PastOrPresent必须是过去的时间,包含现在
@Pattern必须满足正则表达式
@PositiveOrZero正数或0
@Size校验容器的元素个数

单个参数校验

使用很简单只需要在需要校验controller上加上@Validated注解在需校验参数上加上@NotNull,@NotEmpty之类参数校验注解就行了,

1@Validated 2@GetMapping("/home") 3public class ProductController { 4 public Result index(@NotBlank String name, @Email @NotBlank String email) { 5 return ResultResponse.success(); 6 } 7}

对象参数校验

在上面的基础上只需要在对象参数前面加上@Validated注解,然后在需要校验的对象参数的属性上面加上 @NotNull,@NotEmpty之类参数校验注解就行了,

1 @PostMapping("/user") 2public Result index1(@Validated @RequestBody UserParams userParams) { 3 log.info("info test######"); 4 log.error("error test #####"); 5 return ResultResponse.success(userParams); 6 }
1@Data 2public class UserParams { 3 4 @NotBlank 5 private String username; 6 private int age; 7 @NotBlank 8 private String addr; 9 @Email 10 private String email; 11 12 13 14}

参数校验异常信息处理

上面我们进行了参数校验,默认当参数校验没通过后会通过异常方式来抛出错误信息MethodArgumentNotValidException是在校验时抛出的还包括很多其他异常信息,这时我们可以通过全局异常捕获信息来处理这些参数校验异常

全局异常处理类只需要在类上标注@RestControllerAdvice,并在处理相应异常的方法上使用@ExceptionHandler注解,写明处理哪个异常即可

1package cn.soboys.core; 2 3import cn.hutool.core.collection.CollectionUtil; 4import cn.hutool.core.util.StrUtil; 5import cn.soboys.core.authentication.AuthenticationException; 6import cn.soboys.core.ret.Result; 7import cn.soboys.core.ret.ResultCode; 8import cn.soboys.core.ret.ResultResponse; 9import org.springframework.validation.BindException; 10import org.springframework.validation.BindingResult; 11import org.springframework.validation.FieldError; 12import org.springframework.web.HttpMediaTypeNotSupportedException; 13import org.springframework.web.HttpRequestMethodNotSupportedException; 14import org.springframework.web.bind.MethodArgumentNotValidException; 15import org.springframework.web.bind.annotation.ExceptionHandler; 16import org.springframework.web.bind.annotation.RestControllerAdvice; 17import org.springframework.web.servlet.NoHandlerFoundException; 18 19import javax.validation.ConstraintViolation; 20import javax.validation.ConstraintViolationException; 21import javax.validation.Path; 22import java.util.List; 23import java.util.Set; 24import java.util.stream.Collectors; 25 26/** 27 * @author kenx 28 * @version 1.0 29 * @date 2021/6/17 20:19 30 * 全局异常统一处理 31 */ 32@RestControllerAdvice 33public class GlobalExceptionHandler { 34 35 /** 36 * 处理 json 请求体调用接口对象参数校验失败抛出的异常 37 */ 38 @ExceptionHandler(MethodArgumentNotValidException.class) 39 public Result jsonParamsException(MethodArgumentNotValidException e) { 40 BindingResult bindingResult = e.getBindingResult(); 41 List errorList = CollectionUtil.newArrayList(); 42 43 for (FieldError fieldError : bindingResult.getFieldErrors()) { 44 String msg = String.format("%s%s;", fieldError.getField(), fieldError.getDefaultMessage()); 45 errorList.add(msg); 46 } 47 return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, errorList); 48 } 49 50 51 /** 52 * 处理单个参数校验失败抛出的异常 53 */ 54 @ExceptionHandler(ConstraintViolationException.class) 55 public Result ParamsException(ConstraintViolationException e) { 56 57 List errorList = CollectionUtil.newArrayList(); 58 Set<ConstraintViolation<?>> violations = e.getConstraintViolations(); 59 for (ConstraintViolation<?> violation : violations) { 60 StringBuilder message = new StringBuilder(); 61 Path path = violation.getPropertyPath(); 62 String[] pathArr = StrUtil.splitToArray(path.toString(), "."); 63 String msg = message.append(pathArr[1]).append(violation.getMessage()).toString(); 64 errorList.add(msg); 65 } 66 return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, errorList); 67 } 68 69 /** 70 * @param e 71 * @return 处理 form data方式调用接口对象参数校验失败抛出的异常 72 */ 73 @ExceptionHandler(BindException.class) 74 public Result formDaraParamsException(BindException e) { 75 List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors(); 76 List<String> collect = fieldErrors.stream() 77 .map(o -> o.getField() + o.getDefaultMessage()) 78 .collect(Collectors.toList()); 79 return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, collect); 80 } 81 82 /** 83 * 请求方法不被允许异常 84 */ 85 @ExceptionHandler(HttpRequestMethodNotSupportedException.class) 86 public Result httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { 87 return ResultResponse.failure(ResultCode.METHOD_NOT_ALLOWED); 88 } 89 90 /** 91 * @param e 92 * @return Content-Type/Accept 异常 93 * application/json 94 * application/x-www-form-urlencoded 95 */ 96 @ExceptionHandler(HttpMediaTypeNotSupportedException.class) 97 public Result httpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) { 98 return ResultResponse.failure(ResultCode.BAD_REQUEST); 99 } 100 101 /** 102 * handlerMapping 接口不存在跑出异常 103 * 104 * @param e 105 * @return 106 */ 107 @ExceptionHandler(NoHandlerFoundException.class) 108 public Result noHandlerFoundException(NoHandlerFoundException e) { 109 return ResultResponse.failure(ResultCode.NOT_FOUND, e.getMessage()); 110 } 111 112 113 /** 114 * 认证异常 115 * @param e 116 * @return 117 */ 118 @ExceptionHandler(AuthenticationException.class) 119 public Result UnNoException(AuthenticationException e) { 120 return ResultResponse.failure(ResultCode.UNAUTHORIZED,e.getMessage()); 121 } 122 123 /** 124 * 125 * @param e 未知异常捕获 126 * @return 127 */ 128 @ExceptionHandler(Exception.class) 129 public Result UnNoException(Exception e) { 130 return ResultResponse.failure(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage()); 131 } 132}

这里关于全局异常处理请参考我这篇SpringBoot优雅的全局异常处理 这里我返回的是自定义响应体api 请参考我这篇Spring Boot 无侵入式 实现RESTful API接口统一JSON格式返回

当然校验异常的处理还有其它方式,当参数在没有通过校验情况下会帮我们把错误信息注入到BindingResult对象中,会注入到对应controller方法,这个仅限于参数有@RequestBody或者@RequestParam修饰的参数才行

1public String add1(@Validated UserVO userVO, BindingResult result) { 2 List<FieldError> fieldErrors = result.getFieldErrors(); 3 if(!fieldErrors.isEmpty()){ 4 return fieldErrors.get(0).getDefaultMessage(); 5 } 6 return "OK"; 7}

当然我推荐第一种可以通过全局异常处理的方式统一处理校验异常

如果每个Controller方法中都写一遍对BindingResult信息的处理,使用起来还是很繁琐。代码很冗余

当我们写了@validated注解,不写BindingResult的时候,SpringBoot 就会抛出异常。由此,可以写一个全局异常处理类来统一处理这种校验异常,从而免去重复组织异常信息的代码。

关注公众号猿小叔获取更多干货分享 wechart-gongzhonghao

点赞
收藏

评论区

加载中...

相关推荐

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

SpringBoot如何优雅的校验参数

原文地址(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Flqcoder.com%2Fp%2F4cd8a59d.html)原文地址(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Flqcoder.com%2Fp%2

Spring校验@RequestParams和@PathVariables参数

我们在写RestAPI接口时候会用到很多的@RequestParam和@PathVariable进行参数的传递,但是在校验的时候,不像使用@RequestBody那样的直接写在实体类中,我们这篇文章讲解一下如何去校验这些参数。依赖配置要使用JavaValidationAPI,我们必须添加validationap

Java中的参数验证(非Spring版)

1\.Java中的参数验证(非Spring版)1.1.前言为什么我总遇到这种非正常问题,我们知道很多时候我们的参数校验都是放在controller层的传入参数进行校验,我们常用的校验方式就是引入下列的jar包,在参数中添加@Validated,并对Bean对象的参数做不

Spring Boot @ControllerAdvice+@ExceptionHandler处理controller异常

需求:  1.springboot 项目restful 风格统一放回json  2.不在controller写trycatch代码块简洁controller层  3.对异常做统一处理,同时处理@Validated校验器注解的异常方法:  @ControllerAdvice注解定义全局异常处理类@ControllerAdvice

SpringBoot校验参数

前言做web开发有一点很烦人就是要校验参数,基本上每个接口都要对参数进行校验,比如一些格式校验非空校验都是必不可少的。如果参数比较少的话还是容易处理的一但参数比较多了的话代码中就会出现大量的IFELSE就比如下面这样:!(https://oscimg.oschina.net/oscnet/up5b6e114567bc70667