SpringBoot数据校验与优雅处理详解( 二 )

Validator+BindingResult优雅处理默认已经引入相关依赖 。
为实体类定义约束注解/** * 实体类字段加上javax.validation.constraints定义的注解 * @author Summerday */@Data@ToStringpublic class Person {private Integer id;@NotNull@Size(min = 6,max = 12)private String name;@NotNull@Min(20)private Integer age;}使用@Valid或@Validated注解@Valid和@Validated在Controller层做方法参数校验时功能相近 , 具体区别可以往后面看 。
@RestControllerpublic class ValidateController {@PostMapping("/person")public Map validatePerson(@Validated @RequestBody Person person, BindingResult result) {Map map = new HashMap<>();// 如果有参数校验失败 , 会将错误信息封装成对象组装在BindingResult里if (result.hasErrors()) {List res = new ArrayList<>();result.getFieldErrors().forEach(error -> {String field = error.getField();Object value = http://kandian.youth.cn/index/error.getRejectedValue();String msg = error.getDefaultMessage();res.add(String.format("错误字段 -> %s 错误值 -> %s 原因 -> %s", field, value, msg));});map.put("msg", res);return map;}map.put("msg", "success");System.out.println(person);return map;}}发送Post请求 , 伪造不合法数据这里使用IDEA提供的HTTP Client工具发送请求 。
POST http://localhost:8081/personContent-Type: application/json{"name": "hyh","age": 10}响应信息如下:
HTTP/1.1 200 Content-Type: application/jsonTransfer-Encoding: chunkedDate: Sat, 14 Nov 2020 15:58:17 GMTKeep-Alive: timeout=60Connection: keep-alive{"msg": ["错误字段 -> name 错误值 -> hyh 原因 -> 个数必须在6和12之间","错误字段 -> age 错误值 -> 10 原因 -> 最小不能小于20"]}Response code: 200; Time: 393ms; Content length: 92 bytesValidator + 全局异常处理在接口方法中利用BindingResult处理校验数据过程中的信息是一个可行方案 , 但在接口众多的情况下 , 就显得有些冗余 , 我们可以利用全局异常处理 , 捕捉抛出的MethodArgumentNotValidException异常 , 并进行相应的处理 。
定义全局异常处理@RestControllerAdvicepublic class GlobalExceptionHandler {/*** If the bean validation is failed, it will trigger a MethodArgumentNotValidException.*/@ExceptionHandler(MethodArgumentNotValidException.class)public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpStatus status) {BindingResult result = ex.getBindingResult();Map map = new HashMap<>();List list = new LinkedList<>();result.getFieldErrors().forEach(error -> {String field = error.getField();Object value = http://kandian.youth.cn/index/error.getRejectedValue();String msg = error.getDefaultMessage();list.add(String.format("错误字段 -> %s 错误值 -> %s 原因 -> %s", field, value, msg));});map.put("msg", list);return new ResponseEntity<>(map, status);}}定义接口@RestControllerpublic class ValidateController {@PostMapping("/person")public Map validatePerson(@Valid @RequestBody Person person) {Map map = new HashMap<>();map.put("msg", "success");System.out.println(person);return map;}}@Validated精确校验到参数字段有时候 , 我们只想校验某个参数字段 , 并不想校验整个pojo对象 , 我们可以利用@Validated精确校验到某个字段 。
定义接口@RestController@Validatedpublic class OnlyParamsController {@GetMapping("/{id}/{name}")public String test(@PathVariable("id") @Min(1) Long id,@PathVariable("name") @Size(min = 5, max = 10) String name) {return "success";}}发送GET请求 , 伪造不合法信息GET http://localhost:8081/0/hyhContent-Type: application/json未作任何处理 , 响应结果如下:
{"timestamp": "2020-11-15T15:23:29.734+00:00","status": 500,"error": "Internal Server Error","trace": "javax.validation.ConstraintViolationException: test.id: 最小不能小于1, test.name: 个数必须在5和10之间...省略","message": "test.id: 最小不能小于1, test.name: 个数必须在5和10之间","path": "/0/hyh"}【SpringBoot数据校验与优雅处理详解】可以看到 , 校验已经生效 , 但状态和响应错误信息不太正确 , 我们可以通过捕获ConstraintViolationException修改状态 。
捕获异常 , 处理结果@ControllerAdvicepublic class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler {private static final Logger log = LoggerFactory.getLogger(CustomGlobalExceptionHandler.class);/*** If the @Validated is failed, it will trigger a ConstraintViolationException*/@ExceptionHandler(ConstraintViolationException.class)public void constraintViolationException(ConstraintViolationException ex, HttpServletResponse response) throws IOException {ex.getConstraintViolations().forEach(x -> {String message = x.getMessage();Path propertyPath = x.getPropertyPath();Object invalidValue = http://kandian.youth.cn/index/x.getInvalidValue();log.error("错误字段 -> {} 错误值 -> {} 原因 -> {}", propertyPath, invalidValue, message);});response.sendError(HttpStatus.BAD_REQUEST.value());}}