spring boot参数验证

  • 2019 年 10 月 14 日
  • 笔记

必须要知道

  • 简述 JSR303/JSR-349,hibernate validation,spring validation 之间的关系
    JSR303 是一项标准,JSR-349 是其的升级版本,添加了一些新特性,他们规定一些校验规范即校验注解,如 @Null,@NotNull,@Pattern,他们位于 javax.validation.constraints 包下,只提供规范不提供实现;
    hibernate validation 是对这个规范的实践,他提供了相应的实现,并增加了一些其他校验注解,如 @Email,@Length,@Range 等等,他们位于 org.hibernate.validator.constraints 包下;
    spring validation,是对 hibernate validation 进行了二次封装,在 springmvc 模块中添加了自动校验,并将校验信息封装进了特定的类中;

spring mvc对spring validation的应用

get请求 参数验证

@RestController  @RequestMapping("/beavalidate")  public class BeanValidateController {        @GetMapping("/testget")      public ResultBody testGet(@Valid @NotBlank @RequestParam("name") String name){          return ResultBody.successBody("get方式验证成功!");      }  }

post请求 参数验证

@RestController  @RequestMapping("/beavalidate")  public class BeanValidateController {        @PostMapping("/testpost")      public ResultBody testPost(@Valid  @RequestBody TagAddForm tagAddForm){          return ResultBody.successBody("验证成功!");      }  }

参数验证异常统一处理

如果我们没有对验证错误进行处理,调用接口的客户端无法知道到底是什么参数发生了错误,
还有就是如果前后端分离我们一般都是规定json格式传输数据,所以我们最好针对这个错误进行处理并返回格式化的内容。

定义错误相关的枚举

public interface IResult extends Serializable {        Integer getCode();        String getMessage();  }    public enum ResultEnum implements IResult {      /**       * 成功       */      SUCCESS(10000, "成功"),      /**       * 请求参数异常       */      ARGUMENT_EXCEPTION(10001, "请求信息异常");        private Integer code;      private String message;        ResultEnum(Integer code, String msg) {          this.code = code;          this.message = msg;      }        @Override      public Integer getCode() {          return code;      }        @Override      public String getMessage() {          return message;      }        public void setMessage(String message) {          this.message = message;      }  }

定义统一的json对象

@Data  public class ResultBody<T> {      /**       * 错误码       */      private Integer code;        /**       * 提示信息       */      private String message;        /**       * 具体数据       */      private T body;        private ResultBody() {        }        /**       * 构造成功包体       *       * @return 成功的包体       */      public static ResultBody successBody() {          return dowith(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), new HashMap<>(1));      }        /**       * 构造成功的包体       *       * @param body 包体中body的内容       * @return 成功的包体       */      public static ResultBody successBody(Object body) {          return dowith(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), body);      }        /**       * 构造调用失败的包体       *       * @param result 错误码和提示信息内容       * @return 失败的包体       */      public static ResultBody errorBody(IResult result) {          return dowith(result.getCode(), result.getMessage(), new HashMap<>(1));      }        /**       * 构造调用失败的包体       *       * @param result 错误码和提示信息内容       * @param body   包体内容       * @return 失败的包体       */      public static ResultBody errorBody(IResult result, Object body) {          return dowith(result.getCode(), result.getMessage(), body);      }        /**       * 构造调用失败的包体       *       * @param result 错误码和提示信息内容       * @return 失败的包体       */      public static ResultBody errorBody(IResult result, String message) {          return dowith(result.getCode(), message, null);      }        /**       * 构造调用失败的包体       *       * @param result 错误码和提示信息内容       * @param body   包体内容       * @return 失败的包体       */      public static ResultBody errorBody(IResult result, Object body, String message) {          return dowith(result.getCode(), message, body);      }    }  

定义异常处理器

@ControllerAdvice  public class ValidationExceptionHandler {        /**       * 处理post方式参数验证异常       * @param e       * @return       */      @ExceptionHandler({MethodArgumentNotValidException.class})      @ResponseBody      public ResultBody notValidException(MethodArgumentNotValidException e) {          log.error("API参数校验异常:{}", e);          return this.wrapperBindingResult(e.getBindingResult());      }        /**       * 处理get方式参数验证异常       * @param e       * @return       */      @ResponseBody      @ExceptionHandler({MissingServletRequestParameterException.class})      public ResultBody requestMissingParamsErrorHandler(MissingServletRequestParameterException e) {          log.error("MissingServletRequestParameterException:{}", e);          String errorMessage = e.getMessage();          ResultEnum resultEnum = ResultEnum.ARGUMENT_EXCEPTION;            return ResultBody.errorBody(resultEnum);      }        private ResultBody wrapperBindingResult(BindingResult bindingResult) {          ResultEnum resultEnum = ResultEnum.ARGUMENT_EXCEPTION;          StringBuilder errorMessage = new StringBuilder();          if ("prod".equals(this.profile)) {              errorMessage.append("请求信息异常");          } else {              Iterator var4 = bindingResult.getFieldErrors().iterator();                while(var4.hasNext()) {                  FieldError fieldError = (FieldError)var4.next();                  errorMessage.append(fieldError.getField()).append(fieldError.getDefaultMessage()).append(";");              }          }            resultEnum.setMessage(errorMessage.toString());          return ResultBody.errorBody(resultEnum);      }  }

效果

  • get方式参数验证

  • post方式参数验证:

非mvc环境下使用验证

在一些情况下我们并不是只是验证http请求参数的绑定,我们还需要java方法之间调用的参数验证。
不适用验证框架我们一般都是手动验证,但是手动验证是很费力的,下面就介绍一下在非mvc的情况下使用spring validation框架进行验证。

手动验证

定义bean添加验证注解

  @Data  public class TagAddForm {        @NotEmpty(message = "标签名称不能为空")      private String name;        @Min(value = 1,message = "标签类型不能为空")      private int type;    }

编写手动验证逻辑

@Component  public class TagValidate {          /**       * 验证bean的参数       * <p>       *     手动验证       * </p>       */      public void validateBean(){          TagAddForm tagAddForm=new TagAddForm();          ValidatorFactory factory = Validation.buildDefaultValidatorFactory();          Validator validator = factory.getValidator();          Set<ConstraintViolation<TagAddForm>> violations = validator.validate(tagAddForm);          //为空代表验证通过          if (violations.size()==0){              return;          }          for(ConstraintViolation<TagAddForm> violation: violations) {              System.out.println((violation.getMessage()));          }      }      }  

单元测试

@SpringBootTest  @RunWith(SpringRunner.class)  public class TestValidate {      @Autowired      private TagValidate tagValidate;      @Autowired      private UserValidate userValidate;        @Test      public void test(){          tagValidate.validateBean();      }  }

结果:

自动验证

业务类编写

在类上添加注解:@Validated 在方法上添加:@Valid注解

import org.springframework.stereotype.Component;  import org.springframework.validation.annotation.Validated;    import javax.validation.Valid;    @Validated  @Component  public class UserValidate {        public void add(@Valid TagAddForm tagAddForm){          System.out.println("添加标签");      }  }

测试

@SpringBootTest  @RunWith(SpringRunner.class)  public class TestValidate {      @Autowired      private UserValidate userValidate;          @Test      public void test2(){           //new一个 TagAddForm对象作为参数并没有为其赋值          TagAddForm tagAddForm=new TagAddForm();          userValidate.add(tagAddForm);      }  }

结果