前言
咱们都晓得在平时写controller时候,都须要对申请参数进行后端校验,个别咱们可能会这样写
public String add(UserVO userVO) { if(userVO.getAge() == null){ return "年龄不能为空"; } if(userVO.getAge() > 120){ return "年龄不能超过120"; } if(userVO.getName().isEmpty()){ return "用户名不能为空"; } // 省略一堆参数校验... return "OK";}
业务代码还没开始写呢,光参数校验就写了一堆判断。这样写尽管没什么错,然而给人的感觉就是:不优雅,不业余,代码可读性也很差,一看就是新手写的代码
作为久经和平的老司机怎么能这样呢,大神是不容许这样代码呈现的,其实SpringBoot提供整合了参数校验解决方案spring-boot-starter-validation
整合应用
在SpringBootv2.3之前的版本只须要引入 web 依赖就能够了他蕴含了validation校验包在此之后SpringBoot版本就独立进去了须要独自引入依赖
<!--参数校验--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
内置的校验注解有很多,列举如下:
注解 | 校验性能 |
---|---|
@AssertFalse | 必须是false |
@AssertTrue | 必须是true |
@DecimalMax | 小于等于给定的值 |
@DecimalMin | 大于等于给定的值 |
@Digits | 可设定最大整数位数和最大小数位数 |
校验是否合乎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
之类参数校验注解就行了,
@Validated@GetMapping("/home")public class ProductController { public Result index(@NotBlank String name, @Email @NotBlank String email) { return ResultResponse.success(); }}
对象参数校验
在下面的根底上只须要在对象参数后面加上@Validated
注解,而后在须要校验的对象参数的属性下面加上@NotNull,@NotEmpty
之类参数校验注解就行了,
@PostMapping("/user")public Result index1(@Validated @RequestBody UserParams userParams) { log.info("info test######"); log.error("error test #####"); return ResultResponse.success(userParams); }
@Datapublic class UserParams { @NotBlank private String username; private int age; @NotBlank private String addr; @Email private String email;}
参数校验异样信息处理
下面咱们进行了参数校验,默认当参数校验没通过后会通过异样形式来抛出错误信息MethodArgumentNotValidException
是在校验时抛出的还包含很多其余异样信息,这时咱们能够通过全局异样捕捉信息来解决这些参数校验异样
全局异样解决类只须要在类上标注@RestControllerAdvice,并在解决相应异样的办法上应用@ExceptionHandler注解,写明解决哪个异样即可
package cn.soboys.core;import cn.hutool.core.collection.CollectionUtil;import cn.hutool.core.util.StrUtil;import cn.soboys.core.authentication.AuthenticationException;import cn.soboys.core.ret.Result;import cn.soboys.core.ret.ResultCode;import cn.soboys.core.ret.ResultResponse;import org.springframework.validation.BindException;import org.springframework.validation.BindingResult;import org.springframework.validation.FieldError;import org.springframework.web.HttpMediaTypeNotSupportedException;import org.springframework.web.HttpRequestMethodNotSupportedException;import org.springframework.web.bind.MethodArgumentNotValidException;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.RestControllerAdvice;import org.springframework.web.servlet.NoHandlerFoundException;import javax.validation.ConstraintViolation;import javax.validation.ConstraintViolationException;import javax.validation.Path;import java.util.List;import java.util.Set;import java.util.stream.Collectors;/** * @author kenx * @version 1.0 * @date 2021/6/17 20:19 * 全局异样对立解决 */@RestControllerAdvicepublic class GlobalExceptionHandler { /** * 解决 json 申请体调用接口对象参数校验失败抛出的异样 */ @ExceptionHandler(MethodArgumentNotValidException.class) public Result jsonParamsException(MethodArgumentNotValidException e) { BindingResult bindingResult = e.getBindingResult(); List errorList = CollectionUtil.newArrayList(); for (FieldError fieldError : bindingResult.getFieldErrors()) { String msg = String.format("%s%s;", fieldError.getField(), fieldError.getDefaultMessage()); errorList.add(msg); } return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, errorList); } /** * 解决单个参数校验失败抛出的异样 */ @ExceptionHandler(ConstraintViolationException.class) public Result ParamsException(ConstraintViolationException e) { List errorList = CollectionUtil.newArrayList(); Set<ConstraintViolation<?>> violations = e.getConstraintViolations(); for (ConstraintViolation<?> violation : violations) { StringBuilder message = new StringBuilder(); Path path = violation.getPropertyPath(); String[] pathArr = StrUtil.splitToArray(path.toString(), "."); String msg = message.append(pathArr[1]).append(violation.getMessage()).toString(); errorList.add(msg); } return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, errorList); } /** * @param e * @return 解决 form data形式调用接口对象参数校验失败抛出的异样 */ @ExceptionHandler(BindException.class) public Result formDaraParamsException(BindException e) { List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors(); List<String> collect = fieldErrors.stream() .map(o -> o.getField() + o.getDefaultMessage()) .collect(Collectors.toList()); return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, collect); } /** * 申请办法不被容许异样 */ @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public Result httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { return ResultResponse.failure(ResultCode.METHOD_NOT_ALLOWED); } /** * @param e * @return Content-Type/Accept 异样 * application/json * application/x-www-form-urlencoded */ @ExceptionHandler(HttpMediaTypeNotSupportedException.class) public Result httpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) { return ResultResponse.failure(ResultCode.BAD_REQUEST); } /** * handlerMapping 接口不存在跑出异样 * * @param e * @return */ @ExceptionHandler(NoHandlerFoundException.class) public Result noHandlerFoundException(NoHandlerFoundException e) { return ResultResponse.failure(ResultCode.NOT_FOUND, e.getMessage()); } /** * 认证异样 * @param e * @return */ @ExceptionHandler(AuthenticationException.class) public Result UnNoException(AuthenticationException e) { return ResultResponse.failure(ResultCode.UNAUTHORIZED,e.getMessage()); } /** * * @param e 未知异样捕捉 * @return */ @ExceptionHandler(Exception.class) public Result UnNoException(Exception e) { return ResultResponse.failure(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage()); }}
这里对于全局异样解决请参考我这篇SpringBoot优雅的全局异样解决
这里我返回的是自定义响应体api 请参考我这篇Spring Boot 无侵入式 实现RESTful API接口对立JSON格局返回
当然校验异样的解决还有其它形式,当参数在没有通过校验状况下会帮咱们把错误信息注入到BindingResult对象中,会注入到对应controller办法,这个仅限于参数有@RequestBody
或者@RequestParam
润饰的参数才行
public String add1(@Validated UserVO userVO, BindingResult result) { List<FieldError> fieldErrors = result.getFieldErrors(); if(!fieldErrors.isEmpty()){ return fieldErrors.get(0).getDefaultMessage(); } return "OK";}
当然我举荐第一种能够通过全局异样解决的形式对立解决校验异样
如果每个Controller办法中都写一遍对BindingResult信息的解决,应用起来还是很繁琐。代码很冗余
当咱们写了@validated注解,不写BindingResult的时候,SpringBoot 就会抛出异样。由此,能够写一个全局异样解决类来对立解决这种校验异样,从而免去反复组织异样信息的代码。
关注公众号猿人生获取更多干货分享