Spring MVC 数据校验 -实现方式validate自定义注解

前言

自从我接触了spring-boot框架,对于基于注解的开发方式越来越中意,无疑这是一种提升开发效率和简化配置的开发方式。开发中对于方法入参数据校验是必须的也是严谨的,一方面是为了提升性能(对于不合法数据,或格式不正确的数据,不做处理,直接返回错误信息)。另一方面也是业务需求,对某些字段有着check。当我们使用spring框架来开发项目,其实spring-validation这个模块也已经提供了一些常用校验注解。例如@Size、@NotNull、@Email之类的格式或者非空校验。

分析

既然spring-validation这个模块已经提供一部分常用注解,但是在实际开发中的需求,并不能完全解决。
所以我们可以参照既有的注解的实现方式,照葫芦画瓢,也用注解来完成数据字段的校验。

源码分析


@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(NotNull.List.class)
@Documented
@Constraint(
    validatedBy = {}//1.注入校验类
)
public @interface NotNull {
    //2.定义message
    String message() default "{javax.validation.constraints.NotNull.message}";
    Class<?>[] groups() default {};//支持分组校验

    Class<? extends Payload>[] payload() default {};

    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface List {
        NotNull[] value();
    }
}

这个是validation包定义的@NotNull的注解接口的定义
此处需要注意我标注的两处
1.Constraint中注入校验类
2.校验用的message

@Documented
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraint {
    Class<? extends ConstraintValidator<?, ?>>[] validatedBy();//校验类注入
}

此处是@Constraint注解接口的定义
validatedBy 方法中可以传入ConstraintValidator的校验类

public interface ConstraintValidator<A extends Annotation, T> {
    default void initialize(A constraintAnnotation) {
    }

    boolean isValid(T var1, ConstraintValidatorContext var2);//校验实现方法
}

ConstraintValidator校验类接口,具体实现的校验类需要实现这个ConstraintValidator接口,实现isValid这个抽象方法。

分析总结:实装自定义注解校验,需要定义一个注解接口用于标识字段,需要一个ConstraintValidator的实现类,用于判断字段是否满足校验规则

实现

具体实现

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = IpCheckValidator.class)//校验实现类绑定
public @interface IpCheck {

    String message() default "{} ip address is not legal";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface List {
        IpCheck[] value();
    }
}

我这里是自定实现的ip地址check的注解

public class IpCheckValidator implements ConstraintValidator<IpCheck,String> {


    //实现方法
    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        String ipArry[] = value.split("\\.");
       if(ipArry.length==4){
           int ipArryInt[] = new int[4];
           for (int i=0;i<4;i++){
               try {
                   ipArryInt[i]=Integer.parseInt(ipArry[i]);
               } catch (Exception e){
                   return false;
               }
           }
           if(ipArryInt[0]>=1&&ipArryInt[0]<=255)
           for (int j=1;j<4;j++){
               if(ipArryInt[j]<0||ipArryInt[j]>255){
                   return false;
               }
           }
           return  true;
       }
        return false;
    }
}

使用方式

    @IpCheck
    private String ipAddress;

最后的校验信息存在BindingResult,通过hasErrors方法就可以判断校验是否通过

    @RequestMapping(value = "/logon",method =RequestMethod.POST)
    public BaseResponse userRegister(@RequestBody  @Valid RegisterUserInfo userInfo,
                                     BindingResult result){
        //判断校验是否有误
        if(!result.hasErrors()){
            //执行注册
            service.register(userInfo);
        }else{
            //抛出异常-提交参数格式不对
            throw  new APIException(HttpServletResponse.SC_BAD_REQUEST,
                    "request parameters formatter is wrong ");
        }
        return  NORMAL_RESPONSE;
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章