前言
之前参与的新开放平台研发的过程中,由于不同的接口需要对不同的入参进行校验这就涉及到通用参数的校验封装,如果不进行封装,那么写出来的校验代码将会风格不统一、校验工具类不一致、维护风险高等其它因素于是我对其公共的校验做了一个封装,达到了通过注解的方式即可实现参数统一校验
遇到的问题
在封装的时候就发现了一个问题我们是开放平台返回的报文都必须是统一风格也就是类似于{code:999,msg:"参数校验失败",data:null} 这种但是原生的JSR303并不支持自定义的字段所以需要自定义校验注解针对这个问题我参考一些JSR303的资料对其进行了一个定制扩展以达到开发人员不需要关注捕捉和封装返回信息
传统的校验做法
如下校验如果一个实体里面上百个字段需要校验的话,对于维护起来是一个很麻烦的事情而且很多校验可以通过jsr-303的注解方式统一处理无需写一大堆if和else
if(name == null) { //返回错误信息 }else if(age == null) { //返回错误信息 }
基于jsr-303定制后的校验
- 定义一个自定义非空注解
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) @Retention(RUNTIME) @Documented @Constraint(validatedBy = { CorpNotEmptyValidator.class }) public @interface CorpNotEmpty { //自定义字段 String field() default ""; //返回错误码 String code() default "0"; //错误消息 String message() default "{javax.validation.constraints.NotNull.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) @Retention(RUNTIME) @Documented @interface List { CorpNotEmpty[] value(); } }
2.定义非空注解对应的校验器, initialize和isValid作用描述如下:
- initialize方法主要是初始化ReturnCodeModel,用于当校验参数不通过后返回ReturnCodeModel里面主要是封装了返回体如 code,message等
- isValid主要是自定义校验器的校验规则,如下判断是否为空使用 StringUtils.isEmpty方法,如果校验不通过则set flag为false,然后调用基类的isValid方法该基类方法会判断flag是否为false,如果是false说明不通过
public class CorpNotEmptyValidator extends BaseCorpValidator<CorpNotEmpty,String> { @Override public void initialize(CorpNotEmpty constraintAnnotation) { model = new ReturnCodeModel(); model.setCode(constraintAnnotation.code()); model.setErrorMsg(constraintAnnotation.message()); model.setField(constraintAnnotation.field()); } @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { System.out.println("1"); if(StringUtils.isEmpty(s)){ model.setFlag(false); }else{ model.setFlag(true); } return super.isValid(s,constraintValidatorContext); } }
3.定义一个基类,实现 ConstraintValidator,主要是因为需要把isValid这个方法定义成抽象方法提供给不同的校验器使用,避免其它校验器写重复的代码
public abstract class BaseCorpValidator<A extends Annotation,B> implements ConstraintValidator<A ,B> { protected ReturnCodeModel model = null; @Override public boolean isValid(B value, ConstraintValidatorContext context) { if(!model.getFlag()){ context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(JSON.toJSONString(model)).addConstraintViolation(); return false; } return true; } }
4.测试类
public class TestV1 { static ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); static Validator validator = validatorFactory.getValidator(); public static void main(String[] args) { UserModel userModel = new UserModel(); userModel.setName("aa"); userModel.setDate("2011"); Set<ConstraintViolation<UserModel>> constraintViolations = validator.validate(userModel); //判断constraintViolations是否为空不为空说明校验不通过拿到ReturnCodeModel里面的错误信息后返回给客户端 if(!constraintViolations.isEmpty()){ for (ConstraintViolation<?> item : constraintViolations) { ReturnCodeModel codeModel = JSON.parseObject(item.getMessage(), ReturnCodeModel.class); System.out.println(JSON.toJSONString(codeModel)); } } } }
画外音:场景考虑
1.比如name这个字段要满足既不能为空又只能为数字这2个情况如果把2个校验方法都写在同一个校验器则其他开发使用的时候也会影响到所以需要有2个注解的方式一个是校验为空一个是校验是否位数字分析完后那么就存在一个先后顺序问题所以针对这个场景需要自定义一个顺序注解
如下代码在需要校验的model实体上加入@GroupSequence注解这样校验器底层会帮我们按照顺序依次处理
//顺序注解 @GroupSequence({ First.class, Two.class, Three.class, UserModel.class }) public class UserModel { @CorpMustNumber(code="-2",message="必须数字",groups=Two.class)//在执行数字校验 @CorpNotEmpty(code="-1",message="姓名必填",groups=First.class)//先执行非空 private String name; @CorpNotEmpty(code="-1",message="日期必填") private String date; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } }
First
Two
总结
以上就是本篇博客涉及到技术点的所有代码通过定制自己的校验器以满足公司业务场景对于开发来说统一了规范统一风格对以后维护还是扩展都非常方便 如果博文对你有帮助麻烦点个关注或者赞谢谢!17:40:42
相关文章
猜您喜欢
- 01-29每天玩转3分钟 MyBatis-Plus - 4. 高级查询(二)(条件构造器)
- 01-29图像开运算和闭运算
- 01-29《Sequence Models》课堂笔记
- 01-29LeetCode 858 镜面反射
- 01-29百度搜索关键词联想API JSONP使用实例
- 01-29L1和L2:损失函数和正则化