天天看點

JSR303 校驗擴充(分組、按順序校驗)

1.在spring MVC 項目中使用JSR303 校驗資料合法性,一般情況下使用方法為

(1)在接受資料的實體使用注解标添加校驗規則

1 package com.hzsj.wechatdto;
  2 
  3 import org.hibernate.validator.constraints.Length;
  4 import org.hibernate.validator.constraints.NotBlank;
  5 
  6 public class MemberApplyDto {
  7     @NotBlank(message="注冊号不能為空")
  8     @Length(max=6,min=6,message="注冊号必須為6位")
  9     private String registerId;
 10     
 11     @NotBlank(message="姓名不能為空")
 12     @Length(max=50,message="長度不能超過50個字元")
 13     private String name;
 14 
 15     @NotBlank(message="選擇性别")
 16     private String gender;
 17 
 18     @NotBlank(message="請填寫身份證号碼")
 19     @Length(max = 18,message="身份證号碼不能超過18個字元")
 20     private String cardNo;
 21 
 22     @NotBlank(message="請選擇省")
 23     private String province;
 24     
 25     @NotBlank(message="請選擇市")
 26     private String cityName;
 27 
 28     @NotBlank(message="請選擇縣")
 29     private String countyName;
 30 
 31     @NotBlank(message="請填寫詳細位址")
 32     @Length(max=50,message="不能超過50個字元")
 33     private String detailAddress;
 34 
 35     @NotBlank(message="請選擇婚姻狀況")
 36     private String marriage;
 37 
 38     @NotBlank(message="請填寫公司名稱")
 39     @Length(max=30,message="公司名稱不能超過30個字元")
 40     private String companyName;
 41 
 42     @NotBlank(message="請填寫公司電話")
 43     @Length(max=20,message="公司電話不能超過20個字元")
 44     private String companyTel;
 45 
 46     @NotBlank(message="請填寫公司位址")
 47     @Length(max=50,message="公司位址不能超過50個字元")
 48     private String companyAddress;
 49 
 50     @NotBlank(message="請填寫個人履歷")
 51     @Length(max=200,message="個人履歷不能超過200字元")
 52     private String persomResume;
 53 
 54     @NotBlank(message="請選擇管道平台")
 55     private String channelType;
 56 
 57     @NotBlank(message="請填寫保薦人")
 58     @Length(max=20,message="保薦人不能超過20個字元")
 59     private String recommend;
 60     
 61     @NotBlank(message="uuidCode不能為空")
 62     private String uuidCode;
 63     
 64 
 65     public String getRegisterId() {
 66         return registerId;
 67     }
 68 
 69     public void setRegisterId(String registerId) {
 70         this.registerId = registerId;
 71     }
 72 
 73     public String getName() {
 74         return name;
 75     }
 76 
 77     public void setName(String name) {
 78         this.name = name;
 79     }
 80 
 81     public String getGender() {
 82         return gender;
 83     }
 84 
 85     public void setGender(String gender) {
 86         this.gender = gender;
 87     }
 88 
 89     public String getCardNo() {
 90         return cardNo;
 91     }
 92 
 93     public void setCardNo(String cardNo) {
 94         this.cardNo = cardNo;
 95     }
 96 
 97     public String getProvince() {
 98         return province;
 99     }
100 
101     public void setProvince(String province) {
102         this.province = province;
103     }
104 
105     public String getCityName() {
106         return cityName;
107     }
108 
109     public void setCityName(String cityName) {
110         this.cityName = cityName;
111     }
112 
113     public String getCountyName() {
114         return countyName;
115     }
116 
117     public void setCountyName(String countyName) {
118         this.countyName = countyName;
119     }
120 
121     public String getDetailAddress() {
122         return detailAddress;
123     }
124 
125     public void setDetailAddress(String detailAddress) {
126         this.detailAddress = detailAddress;
127     }
128 
129     public String getMarriage() {
130         return marriage;
131     }
132 
133     public void setMarriage(String marriage) {
134         this.marriage = marriage;
135     }
136 
137     public String getCompanyName() {
138         return companyName;
139     }
140 
141     public void setCompanyName(String companyName) {
142         this.companyName = companyName;
143     }
144 
145     public String getCompanyTel() {
146         return companyTel;
147     }
148 
149     public void setCompanyTel(String companyTel) {
150         this.companyTel = companyTel;
151     }
152 
153     public String getCompanyAddress() {
154         return companyAddress;
155     }
156 
157     public void setCompanyAddress(String companyAddress) {
158         this.companyAddress = companyAddress;
159     }
160 
161     public String getPersomResume() {
162         return persomResume;
163     }
164 
165     public void setPersomResume(String persomResume) {
166         this.persomResume = persomResume;
167     }
168 
169     public String getChannelType() {
170         return channelType;
171     }
172 
173     public void setChannelType(String channelType) {
174         this.channelType = channelType;
175     }
176 
177     public String getRecommend() {
178         return recommend;
179     }
180 
181     public void setRecommend(String recommend) {
182         this.recommend = recommend;
183     }
184 
185     public String getUuidCode() {
186         return uuidCode;
187     }
188 
189     public void setUuidCode(String uuidCode) {
190         this.uuidCode = uuidCode;
191     }
192 
193     
194 }      

(2)在Controller中使用BindResult 接收校驗的結果

@RequestMapping(value="/apply",method=RequestMethod.POST)
    @ResponseBody
    public ResultVo memberApply(@Valid MemberApplyDto dto,BindingResult bindingResult,Errors errors){
        ResultVo<Object> resultVo = new ResultVo<>();
        if(errors.hasErrors()){
            List<FieldError> errorsList = bindingResult.getFieldErrors();
            Map<String, String> map = new HashMap<>();
            for(FieldError fieldError:errorsList){
                map.put(fieldError.getField(), fieldError.getDefaultMessage());
            }
            resultVo.setCode(StatusEnums.DATAVALID_ERROR.getCode());
            resultVo.setData(map);
            resultVo.setMsg(StatusEnums.DATAVALID_ERROR.getMsg());
            return resultVo;
        }
        LoginVo vo = memberApplyService.submitApply(dto);
        resultVo.setCode(StatusEnums.SUCCESS.getCode());
        resultVo.setMsg(StatusEnums.SUCCESS.getMsg());
        resultVo.setData(vo);
        return resultVo;
    }      

2.如果沒有特殊需求的情況下使用上面的校驗即可。但是遇到其他情況上面的校驗就不能滿足或者不能靈活應對了。例如

(1)實體中的字段校驗按照順序進行,如果第一個字段校驗失敗,則接下來的校驗不再進行。

(2)實體的字段校驗按情況分類,分組校驗,再不同方法中校驗的字段和順序不同。

(3)自定義校驗規則。

剛好今天遇到上面的三種情況,接下來用執行個體一一解答。針對上面的需求,JSR303校驗中專門提供了group (驗證規則所屬組)和 @GroupSequence(驗證組的順序) 來實作。

我的需求是表單中的字段按個按順序校驗,如果有前面的字段校驗失敗,則中斷校驗并傳回校驗結果。我的表單中有十幾個字段,我的想法是為每個字段定義一個組,然後按照組的順序進行校驗(如果字段很多會比較麻煩,暫時不知道有什麼更好的辦法, 如果有人知道的話請指教。)于是上面的實體類就變成了下面的樣子

package com.hzsj.wechatdto;

import java.io.Serializable;

import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;

import com.hzsj.common.util.annotation.IsUndefined;
public class MemberApplyDto implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = -7063091764413674200L;

    @NotBlank(message="注冊号不能為空",groups={Default.class})
    @Length(max=6,min=6,message="注冊号必須為6位",groups={Default.class})
    private String registerId;
    
    @NotBlank(message="請填寫姓名",groups={Validate1.class})
    @Length(max=50,message="長度不能超過50個字元",groups={Validate1.class})
    private String name;

    @NotBlank(message="選擇性别",groups={Validate2.class})
    private String gender;

    @NotBlank(message="請填寫身份證号碼",groups={Validate3.class})
    @Length(max = 18,message="身份證号碼不能超過18個字元",groups={Validate3.class})
    private String cardNo;

    @NotBlank(message="請選擇戶籍地區",groups={Validate4.class})
    @IsUndefined(message="請選擇戶籍地區",groups={Validate4.class})
    private String province;
    
    @NotBlank(message="請選擇戶籍地區",groups={Validate5.class})
    @IsUndefined(message="請選擇戶籍地區",groups={Validate5.class})
    private String cityName;

    @NotBlank(message="請選擇戶籍地區",groups={Validate6.class})
    @IsUndefined(message="請選擇戶籍地區",groups={Validate6.class})
    private String countyName;

    @NotBlank(message="請填寫詳細位址",groups={Validate7.class})
    @Length(max=50,message="不能超過50個字元",groups={Validate7.class})
    private String detailAddress;

    @NotBlank(message="請選擇婚姻狀況",groups={Validate8.class})
    private String marriage;

    @NotBlank(message="請填寫公司名稱",groups={Validate9.class})
    @Length(max=30,message="公司名稱不能超過30個字元",groups={Validate9.class})
    private String companyName;

    @NotBlank(message="請填寫公司電話",groups={Validate10.class})
    @Length(max=20,message="公司電話不能超過20個字元",groups={Validate10.class})
    private String companyTel;

    @NotBlank(message="請填寫公司位址",groups={Validate11.class})
    @Length(max=50,message="公司位址不能超過50個字元",groups={Validate11.class})
    private String companyAddress;

    @NotBlank(message="請填寫個人履曆",groups={Validate12.class})
    @Length(max=200,message="個人履曆不能超過200字元",groups={Validate12.class})
    private String persomResume;

    @NotBlank(message="請選擇管道平台",groups={Validate13.class})
    private String channelType;

    @NotBlank(message="請填寫保薦人",groups={Validate14.class})
    @Length(max=20,message="保薦人不能超過20個字元",groups={Validate14.class})
    private String recommend;
    
    @NotBlank(message="uuidCode不能為空",groups={Default.class})
    private String uuidCode;
    

    public String getRegisterId() {
        return registerId;
    }

    public void setRegisterId(String registerId) {
        this.registerId = registerId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getCardNo() {
        return cardNo;
    }

    public void setCardNo(String cardNo) {
        this.cardNo = cardNo;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String cityName) {
        this.cityName = cityName;
    }

    public String getCountyName() {
        return countyName;
    }

    public void setCountyName(String countyName) {
        this.countyName = countyName;
    }

    public String getDetailAddress() {
        return detailAddress;
    }

    public void setDetailAddress(String detailAddress) {
        this.detailAddress = detailAddress;
    }

    public String getMarriage() {
        return marriage;
    }

    public void setMarriage(String marriage) {
        this.marriage = marriage;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public String getCompanyTel() {
        return companyTel;
    }

    public void setCompanyTel(String companyTel) {
        this.companyTel = companyTel;
    }

    public String getCompanyAddress() {
        return companyAddress;
    }

    public void setCompanyAddress(String companyAddress) {
        this.companyAddress = companyAddress;
    }

    public String getPersomResume() {
        return persomResume;
    }

    public void setPersomResume(String persomResume) {
        this.persomResume = persomResume;
    }

    public String getChannelType() {
        return channelType;
    }

    public void setChannelType(String channelType) {
        this.channelType = channelType;
    }

    public String getRecommend() {
        return recommend;
    }

    public void setRecommend(String recommend) {
        this.recommend = recommend;
    }

    public String getUuidCode() {
        return uuidCode;
    }

    public void setUuidCode(String uuidCode) {
        this.uuidCode = uuidCode;
    }

    public interface Validate1{};
    public interface Validate2{};
    public interface Validate3{};
    public interface Validate4{};
    public interface Validate5{};
    public interface Validate6{};
    public interface Validate7{};
    public interface Validate8{};
    public interface Validate9{};
    public interface Validate10{};
    public interface Validate11{};
    public interface Validate12{};
    public interface Validate13{};
    public interface Validate14{};
    public interface Default{};
}      

其中特别說明:實體類中的這些 interface 用來定義一個驗證組,類似一個辨別。然後為每個字段指定相應的驗證組,其餘字段使用預設的驗證組。

接下來聲明一個驗證序列,指定這個序列需要驗證哪些組和驗證的順序。

package com.hzsj.wechatdto;

import javax.validation.GroupSequence;

@GroupSequence(value={MemberApplyDto.Validate1.class,
        MemberApplyDto.Validate2.class,
        MemberApplyDto.Validate3.class,
        MemberApplyDto.Validate4.class,
        MemberApplyDto.Validate5.class,
        MemberApplyDto.Validate6.class,
        MemberApplyDto.Validate7.class,
        MemberApplyDto.Validate8.class,
        MemberApplyDto.Validate9.class,
        MemberApplyDto.Validate10.class,
        MemberApplyDto.Validate11.class,
        MemberApplyDto.Validate12.class,
        MemberApplyDto.Validate13.class,
        MemberApplyDto.Validate14.class,
        MemberApplyDto.Default.class,

})
public interface ApplySequence {

}      

我指定的是驗證所有組,并按照組的順序驗證。如果在某些情況下隻需要驗證其中部分字段的話,可重新定義一個驗證序列,在接下的Controller中去使用這個序列。

@RequestMapping(value="/apply",method=RequestMethod.POST)
    @ResponseBody
    public ResultVo memberApply(@Validated({ApplySequence.class}) MemberApplyDto dto,BindingResult bindingResult,Errors errors){
        ResultVo<Object> resultVo = new ResultVo<>();
        if(errors.hasErrors()){
            List<FieldError> errorsList = bindingResult.getFieldErrors();
            Map<String, String> map = new HashMap<>();
            for(FieldError fieldError:errorsList){
                map.put(fieldError.getField(), fieldError.getDefaultMessage());
            }
            resultVo.setCode(StatusEnums.DATAVALID_ERROR.getCode());
            resultVo.setData(map);
            resultVo.setMsg(StatusEnums.DATAVALID_ERROR.getMsg());
            return resultVo;
        }
        LoginVo vo = memberApplyService.submitApply(dto);
        resultVo.setCode(StatusEnums.SUCCESS.getCode());
        resultVo.setMsg(StatusEnums.SUCCESS.getMsg());
        resultVo.setData(vo);
        return resultVo;
    }      

在Controller中需要的注意的是将原來的@Valid 替換成@Validated 。同時指定了我所需要的驗證序列是按照自己定義的驗證序列。

@Valid是javax.validation裡的。

@Validated是@Valid 的一次封裝,是Spring提供的校驗機制使用。

相比@Valid  @Validated 提供了幾個新功能

(1)可以通過groups對驗證進行分組

(2)按照序列組來驗證

(3)驗證多個實體

到此基本實作了按照順序按個驗證字段的合法性,但是同時發現了另外的一種情況,前端字段為空的時候會傳過來的undefined,導緻原來的驗證規則失效。所有我們需要自己去定義一個驗證規則去驗證undefined。上面的實體使用的 @IsUndefined 就是我自行定義的。

首先定義一個注解,同時指定實作校驗規則的類 validatedBy = {UndefinedValiadator.class}

package com.hzsj.common.util.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER,ElementType.CONSTRUCTOR,ElementType.ANNOTATION_TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {UndefinedValiadator.class})
public @interface IsUndefined {
    
        //提示資訊
        String message() default "";

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

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

其次,實作這個校驗規則

package com.hzsj.common.util.annotation;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.springframework.util.StringUtils;

public class UndefinedValiadator implements ConstraintValidator<IsUndefined,String>{
    
    
    @Override
    public void initialize(IsUndefined constraintAnnotation) {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(StringUtils.isEmpty(value)){
            return false;
        }
        if("undefined".equals(value)){
            return false;
        }else{
            return true;
        }
    }

}      

至此完成了一個自定義的規則,可以在自己的實體類中去使用了

轉載于:https://www.cnblogs.com/li-zhi-long/p/10530321.html