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;
    }

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