在开发中,我们常常使用javax.validation包下提供的注解来对前端传过来的字段进行校验,这种方法大大降低了我们反复书写常见校验的代码。
传统写法:
实体侧:
@Data @Accessors(chain = true) @ApiModel(value = "People",description = "人") public class People{ @ApiModelProperty(value = "姓名") private String name; @ApiModelProperty(value = "性别") private String sex; @ApiModelProperty(value = "年龄") private Integer age; @ApiModelProperty(value = "孩子") private List<Child> chidren; }
请求校验测:
@Api(value = "人",tags = "人") @RestController @RequestMapping("/people") public class PeopleController{ boolean validateParams(People peopleRequest,String type){ //提交时必须不为空,如果作为草稿保存时可以为空 if(StrUtil.isBlank(peopleRequest.getName())) { return false; } if(StrUtil.isBlank(peopleRequest.getSex())){ return false; } if(ObjectUtil.isNull(peopleRequest.getAge())){ return false; } if(CollectionUtil.isEmpty(children)){ return false; } return true; } /** * 保存草稿 */ @ApiOperation(value = "保存人草稿信息") public BaseResponse<void> draft(@RequestBody @Validated(People.draft.class) People peopleRequst){ } /** * 提交 */ @ApiOperation(value = "提交人信息") public BaseResponse<void> submit(@RequestBody @Validated(People.submit.class) People peopleRequst){ if(!validateParams(peopleRequest,"submit")){ BaseResponse.error("参数错误"); } } }
javax.validation注解简化后:
实体测:
@Data @Accessors(chain = true) @ApiModel(value = "People",description = "人") public class People{ @NotBlank(message = "姓名不能为空",groups = {People.submit.class}) @ApiModelProperty(value = "姓名",required = true) private String name; @NotBlank(message = "性别不能为空",groups = {People.submit.class}) @ApiModelProperty(value = "性别",required = true) private String sex; @NotNull(message = "年龄不能为空",groups = {People.submit.class}) @ApiModelProperty(value = "年龄",required = true) private Integer age; @NotEmpty(message = "孩子不能为空",groups = {People.submit.class}) @ApiModelProperty(value = "孩子",required = true) private List<Child> chidren; //构建不同情况(分组) //提交 public @interface submit{} //保存草稿 public @interface draft{} }
请求校验测:
@Api(value = "人",tags = "人") @RestController @RequestMapping("/people") public class PeopleController{ /** * 保存草稿 */ @ApiOperation(value = "保存人草稿信息") public BaseResponse<void> draft(@RequestBody @Validated(People.draft.class) People peopleRequst){} /** * 提交 */ @ApiOperation(value = "提交人信息") public BaseResponse<void> submit(@RequestBody @Validated(People.submit.class) People peopleRequst){} }
使用到的注解:
@NotNull(message = "") //适用于除了基本数据类型的其他类型数据进行空校验 @NotBlank(message = "")//适用于字符串类型数据,除了会校验为空,还会校验是否为空字符 @NotEmpty(message = "")//适用于集合类型数据,会校验是否为空,还会校验集合长度是否为0,即集合是否有元素 @Validate //一般在控制器方法参数前加,表示进行校验
特殊情况:
这种情况保存草稿和提交接口是分开的,所以使用javax.validation注解@Validated可以很好的解决不同场景进行不同校验的要求,但是若是这两个接口需要合并成一个接口呢?
这种情况可能又必须使用传统的方法了,专门编写一个函数对每个字段进行校验!!!
这样实在是太麻烦了,那我们为什么不专门写一个工具类对字段进行javax的校验呢?
最终代码:
实体类:
@Data @Accessors(chain = true) @ApiModel(value = "People",description = "人") public class People{ @NotBlank(message = "姓名不能为空") @ApiModelProperty(value = "姓名") private String name; @NotBlank(message = "性别不能为空") @ApiModelProperty(value = "性别") private String sex; @NotNull(message = "年龄不能为空") @ApiModelProperty(value = "年龄") private Integer age; @NotEmpty(message = "孩子不能为空") @ApiModelProperty(value = "孩子") private List<Child> chidren; }
控制器:
@Api(value = "人",tags = "人") @RestController @RequestMapping("/people") public class PeopleController{ /** * 保存草稿 */ @ApiOperation(value = "保存或提交人信息") public BaseResponse<void> draftOrSubmit(@RequestBody People peopleRequst,String type){ if("submit".equals(type)){ CommonUtils.validateData(peopleRequest); } } }
工具类:
public class CommonUtils{ @SneakyThrows public static void validateData(Object obj){ //获取对象的Class类型 Class<?> clazz = obj.getClass(); //获取对象的所有字段 Field[] fields = clazz.getDeclaredFields(); for(Field field : fields){ //设置字段可访问 field.setAccessible(true); //获取字段的值 Object value = field.get(obj); //NOT NULL 校验 if(field.isAnnotationPresent(NotNull.class) && ObjectUtils.isEmpty(value)){ throw new CommonException(900009,field.getAnnotation(NotNull.class).message()); } //NOT Blank校验 else if(field.isAnnotationPresent(NotBlank.class)&& StringUtils.isEmpty(String.valueOf(value))){ throw new CommonException(900009,field.getAnnotation(NotBlank.class).message()); } //LIST SIZE 校验 else if(field.isAnnotationPresent(Size.class)){ Size sizeAnnotation = field.getAnnotation(Size.class); if(sizeAnnotation.min()>((Collection<?>) value).size() || sizeAnnotation.max() < ((Collection<?>) value).size()){ throw new CommonException(900009,sizeAnnotation.message()); } } //MAX校验 else if(field.isAnnotationPresent(Max.class)){ if(field.getAnnotation(Max.class).value() < (Integer) value){ throw new CommonException(900009,field.getAnnotation(Max.class).message()); } } //MIN校验 else if(field.isAnnotationPresent(Min.class)){ if(field.getAnnotation(Min.class).value()< (Integer) value){ throw new CommonException(900009,field.getAnnotation(Min.class).message()); } } //LENGTH校验 else if(field.isAnnotationPresent(Length.class)){ if(ObjectUtils.isEmpty(value)){ return; } Length lengthAnnotation = field.getAnnotation(Length.class); if(lengthAnnotation.min()> String.valueOf(value).length() || lengthAnnotation.max()<String.valueOf(value).length()){ throw new CommonException(900009,lengthAnnotation.message()); } } } } }
工具类主要使用了java反射完成对加上javax.validation注解的字段的校验。
之后在代码的任何位置我们都可以使用CommonUtils.validateData()
对请求类进行校验了,这样就自由了需要,不要再受到@Validation注解的限制了。我们也可以通过在validateData方法中编写自己的逻辑对某些注解进行自定义校验。