使用spring的@Valid注解加在接口参数上的类上,然后在类的属性上加上实现了JSR-303规范的注解即可实现校验对象的属性
- JSR-303是一个数据验证的规范,但是spring并没有对其进行实现,Hibernate Validator是实现了这一规范的
- 我们springboot项目里面是已经引入了hibernate-validator的包,里面包含了实现JSR303规范的注解
我们点击进去源码可以看到validation-api包的已有很多实现JSR-303规范的注解
注解名称 | 功能 |
---|---|
@Null | 验证对象是否为null |
@NotNull | 验证对象是否不为null, 无法查检长度为0的字符串 |
@NotBlank | 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格 |
@NotEmpty | 检查约束元素是否为NULL或者是EMPTY |
@AssertTrue | 验证 Boolean 对象是否为 true |
@AssertFalse | 验证 Boolean 对象是否为 false |
@Size(min=, max=) | 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内 |
@Length(min=, max=) | 验证字符串的长度是否在给定的范围之内 |
@Past | 验证 Date 和 Calendar 对象是否在当前时间之前 |
@Future | 验证 Date 和 Calendar 对象是否在当前时间之后 |
@Pattern | 验证 String 对象是否符合正则表达式的规则 |
@Min | 验证 Number 和 String 对象是否大等于指定的值 |
@Max | 验证 Number 和 String 对象是否小等于指定的值 |
@DecimalMax | 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度 |
@DecimalMin | 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度 |
@Digits | 验证 Number 和 String 的构成是否合法 |
验证是否是邮件地址,如果为null,不进行验证,算通过验证 | |
@PositiveOrZero | 校验参数必须是正整数或0。 |
虽然现在有很多很有用的验证注解,如@not null,@not blank,@not empty.@Email等等。但是我们可能有时候根据项目的拓展这些验证注解不够用,,我们可能还需要自己的自定义验证注解,
刚刚的validation-api包里面还有一个@Constraint注解,我们的自定义注解里面加上这个注解就能实现自定义验证
1)创建一个自定义验证注解
我们这个自定义注解实现一个简单的功能:
根据type参数进行校验,校验为空、年龄范围、手机号码
@Constraint(validatedBy = {CheckValidator.class})//指向实现验证的类
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {CheckValidator.class})//指向实现验证的类
public @interface Check {
CheckValidatorEnum type() default CheckValidatorEnum.Null;
long min() default 1;
long max() default 1;
String message() default "参数异常";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
//校验枚举类
@Getter
public enum CheckValidatorEnum {
Null(1,"为空校验"),
AGE(2,"年龄校验"),
Phone(3,"手机校验");
CheckValidatorEnum(int code,String desc){
this.code = code;
this.desc = desc;
}
private int code;
private String desc;
}
2)实现验证的类
我们这个实现自定义验证的类需要实现ConstrainValidator接口
public class CheckValidator implements ConstraintValidator<Check,Object> {
private Check check;
private CheckValidatorEnum checkValidatorEnum;
@Override
public void initialize(Check CheckAnnotation){
this.check = CheckAnnotation;
this.checkValidatorEnum = CheckAnnotation.type();
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
if(checkValidatorEnum.getCode()==CheckValidatorEnum.Null.getCode()){
//非空校验
if(o != null && !o.toString().equals("")){
return true;
}
}else if(checkValidatorEnum.getCode()==CheckValidatorEnum.AGE.getCode()){
//年龄校验
if(o.toString() == null){
return true;
}
long min = this.check.min();
long max = this.check.max();
Integer age = Integer.parseInt(o.toString());
if(age>=min && age<=max){
return true;
}
}else if(checkValidatorEnum.getCode()==CheckValidatorEnum.Phone.getCode()){
if(o == null){
return true;
}
//手机号码校验
if(CommonUtil.isMobile(o.toString())){
return true;
}
}
return false;
}
}
3)使用@Valid注解加在类上校验
如下 @Valid User user,同时在User里面的属性加入自定义注解
public ResponseObject addUser(@Valid @RequestBody User user) throws IOException {}
@Data
public class User {
private int id;
@Check(type = CheckValidatorEnum.Null)
private String name;
@Check(type = CheckValidatorEnum.AGE, min = 18,max = 30,message = "年龄不在18-30范围内")
private Integer age;
@Check(type = CheckValidatorEnum.Phone,message = "手机号码不正确")
private String tel;
@Valid
private UserCityInfo cityInfo;
}
//嵌套对象的校验
@Data
public class UserCityInfo {
@Check(type = CheckValidatorEnum.Null,message = "城市编码不能为空")
private Integer cityCode;
@NotEmpty
private String cityName;
}
备注:
可能需要校验的对象的属性还是对象,然后我们需要校验对象里面的对象,这种嵌套校验,我们只需要在对象的属性对象上再加一个@Valid即可实现嵌套校验
4)在全局异常里捕获校验异常
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandle {
//捕获@Valid校验不通过的异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseObject handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error(e.getMessage(), e);
BindingResult ex = e.getBindingResult();
List<ObjectError> allErrors = ex.getAllErrors();
ObjectError error = allErrors.get(0);
//获取自定义校验注解的异常信息
String defaultMessage = error.getDefaultMessage();
ResponseObject responseObject = new ResponseObject(ResultStatusCode.VALIDATE_FAIL.getStatuscode(),defaultMessage,null);
return responseObject;
}
@ExceptionHandler(Exception.class)
public ResponseObject handleException(Exception e) {
log.error(e.getMessage(), e);
ResponseObject responseObject = new ResponseObject(ResultStatusCode.SYSTEM_ERROR.getStatuscode(),ResultStatusCode.SYSTEM_ERROR.getStatusmsg(),null);
return responseObject;
}
}
然后我们就实现了在注解里校验,不需要在接口代码里再判断
5)controller入口,在入参的model对象增加@Valid 注解
注:在使用@Vliad的时候就会拦截校验这些有@Constraint注解的属性
/**
* 增加用户
* @return
* @throws IOException
*/
@XinLinLog(value = "增加用户")
@RequestMapping(value="/addUser", method=RequestMethod.POST)
public ResponseObject addUser(@Valid @RequestBody User user) throws IOException {
ResponseObject result = new ResponseObject();
JSONObject msg = new JSONObject();
msg.put("user",user);
result.setResultObject(msg);
return result;
}