springboot中参数验证自定义注解,@Valid总结

使用spring的@Valid注解加在接口参数上的类上,然后在类的属性上加上实现了JSR-303规范的注解即可实现校验对象的属性

  1. JSR-303是一个数据验证的规范,但是spring并没有对其进行实现,Hibernate Validator是实现了这一规范的
  2. 我们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 的构成是否合法
@Email验证是否是邮件地址,如果为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;
    }

在这里插入图片描述

  • 7
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值