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); } }
结果