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; } } }
至此完成了一个自定义的规则,可以在自己的实体类中去使用了