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; } } }
至此完成了一個自定義的規則,可以在自己的實體類中去使用了