JSR 303 – Bean Validation 是一个数据验证的规范,2009 年 11 月确定最终方案。 Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 我们通常写接口时会对传来的参数进行校验判断,比如字符串非空判断,值在多少返回等,这些就要用到Spring 的参数校验,这里我们使用在spring-boot-starter-web包里面有hibernate-validator包, 参数校验有几种方式,如下
下面来介绍这两种方式的使用
1.符合国际JSP 303规范,先定义个Bean,别人在字段名上添加验证的注解,@NotBlank 字段名不能为null,同时长度大于0常有的校验类型有
Constraint 详细信息 @Null 被注释的元素必须为 null @NotNull 被注释的元素必须不为 null @AssertTrue 被注释的元素必须为 true @AssertFalse 被注释的元素必须为 false @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 @Size(max, min) 被注释的元素的大小必须在指定的范围内 @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内 @Past 被注释的元素必须是一个过去的日期 @Future 被注释的元素必须是一个将来的日期 @Pattern(value) 被注释的元素必须符合指定的正则表达式
/** * @author Lang.Chen * @date 2018/6/20 下午4:39 */ public class User { //账号 @NotBlank(message = "账号不能为空") private String phone; //密码 @NotBlank(message = "密码不能为空") private String password; public void setPhone(String phone) { this.phone = phone; } public void setPassword(String password) { this.password = password; } public String getPhone() { return phone; } public String getPassword() { return password; } }
2.在Controller类里面对相应的接口添加@Valid+BindingResult验证,加完以后,如果参数验证不通过,那就直接进入if语句里面,在语句里面做相应的返回结果
if(bindingResult.hasErrors()){ }
@RestController @RequestMapping(value = "/user") public class UserController implements IUser { @Override @RequestMapping(value = "/login", method = RequestMethod.POST) public User login( @Valid User userInfo, BindingResult bindingResult) { if (bindingResult.hasErrors()) { System.out.print(bindingResult.getFieldError().getDefaultMessage()); return null; } return userInfo; } }
好了,第一种验证我们已经说完了,但你仔细想想,如果我有多个接口,是不是每次有要写@Valid+BindgResult bingingResult,然后再到If语句里面进行判断,如果仅仅是错误信息不一致,但返回的客户端结构是一致的,比如
{"code":"1002","message":"parameters are missing","data":"{}"}
不同的验证只要改变message,那么是不是可以统一处理,来减少代码量,于是下面介绍第二种方式
简单点说,由第一种方式去掉BindingResult,然后再定义一个全局异常类,同样的
/** * @author Lang.Chen * @date 2018/6/20 下午4:39 */ public class User { //账号 @NotBlank(message = "账号不能为空") private String phone; //密码 @NotBlank(message = "密码不能为空") private String password; public void setPhone(String phone) { this.phone = phone; } public void setPassword(String password) { this.password = password; } public String getPhone() { return phone; } public String getPassword() { return password; } }
public class UserController implements IUser { @Override @RequestMapping(value = "/login2", method = RequestMethod.POST) public User login2(@RequestBody @Valid User userInfo) { return null; } }
所有验证失败的结果都会在GlobleExceptionHandler的defultExcepitonHandler方法里面捕捉到
@RestControllerAdvice @Component public class GlobleExceptionHandler { @ResponseBody @ExceptionHandler(Exception.class) public String defultExcepitonHandler(Exception ex) { ex.printStackTrace(); if(ex instanceof BindException){ //处理返回的错误信息 StringBuffer errorMsg = new StringBuffer(); BindException c = (BindException) ex; List<ObjectError> errors = c.getBindingResult().getAllErrors(); for (ObjectError error : errors) { errorMsg.append(error.getDefaultMessage()).append(";"); } return errorMsg.toString(); } return ""; } }
有关于自定义验证和分组验证,就不一一介绍了,会使用以上2个就可以了。
有时候只传入少数验证时,我们不可能都是新建一个对象来接受,这时候如何使用单个参数验证呢,看到网上使用在类上添加 @Validated参数,然后通过全局异常捕捉方式
@RestController @RequestMapping("/order") @Validated public class OrderController implements IOrder { @RequestMapping(value = "/byId",method = RequestMethod.POST) public OrderInfo byId( @Max(value = 50) int orderId) { return null; } }
但每次访问接口时,都是报同一个错误,就没用这个方式了,如果有人找到解决方案,请指教,谢谢
{ "timestamp": "2018-06-24T07:09:14.235+0000", "status": 404, "error": "Not Found", "message": "No message available", "path": "/order/byId" }
@Valid是javax.validation里的 @Validated是@Valid 的一次封装,是Spring提供的校验机制使用。@Validated 提供分组功能