美文网首页
参数校验工具之Validator自定义校验

参数校验工具之Validator自定义校验

作者: 从入门到脱发 | 来源:发表于2019-03-05 15:16 被阅读0次

    validator百度博客一大堆,本文是摘取的部分内容结合在项目中使用的经验.

    一.导入maven依赖

        <dependency> 
          <groupId>javax.validation</groupId>  
          <artifactId>validation-api</artifactId>  
          <version>1.1.0.Final</version> 
        </dependency>  
        <dependency> 
          <groupId>org.hibernate</groupId>  
          <artifactId>hibernate-validator</artifactId>  
          <version>5.2.0.Final</version> 
        </dependency> 
    

    二.给需要校验的pojo加上注解

    @Data
    public class InformationBasicInfoAO {
       /**
        * 主键id
        */
       private Long id;
       /**
        * 文章标题
        */
       @NotBlank(message = "文章标题不能为空")
       private String title;
       /**
        * 发布日期
        */
       @NotNull(message = "发布时间不能为空")
       private Date releaseDate;
       /**
        * 文章来源
        */
       @NotBlank(message = "文章来源不能为空")
       private String source;
    

    message的内容是返回给前端的提醒信息,可以不填

    三.校验

    1.通用的校验工具
    @Slf4j
    public class ValidatorUtil {
        //也可以使用spring注入的方式
        private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        /**
         * 使用指定分组
         *
         * @param object 被校验的bean
         * @param groups 分组
         * @return
         */
        public static <T> Map<String, StringBuilder> validate(T object, Class<?>... groups) {
            Map<String, StringBuilder> errorMap = new HashMap<>(16);
            if (groups == null) {
                groups = new Class[]{Default.class};
            }
            Set<ConstraintViolation<T>> set = validator.validate(object, groups);
            if (CollectionUtils.isEmpty(set)) {
                return null;
            }
            String property;
            for (ConstraintViolation<T> c : set) {
                // 这里循环获取错误信息,可以自定义格式
                property = c.getPropertyPath().toString();
                if (errorMap.get(property) != null) {
                    errorMap.get(property).append(",").append(c.getMessage());
                } else {
                    StringBuilder sb = new StringBuilder();
                    sb.append(c.getMessage());
                    errorMap.put(property, sb);
                }
            }
            return errorMap;
        }
    }
    
    2.使用工具校验

    这种方式直接抛去异常,全局异常处理

            Map<String, StringBuilder> errorMap = ValidatorUtil.validate(object);
            if (!CollectionUtils.isEmpty(errorMap)) {
                log.info("------参数校验失败:{}", errorMap);
                throw new ApplicationException(new ExceptionContext("参数校验失败"), "000005", errorMap.toString());
            }
    

    也可以在controller中返回.

          Map<String, StringBuilder> errorMap = ValidatorUtils.validate(accountAO);
          if (!CollectionUtils.isEmpty(errorMap)) {
              simpleData.setData(errorMap);
              result.setContent(simpleData);
              return result;
          }
    

    也可以在controller的形参上使用注解@Valid,这种方式不需要util类,可以直接使用,直接抛出异常,需要加配置项,具体可以百度,个人不喜欢这种方式,不够灵活.

        @PostMapping(value = "/order")
        public PojoResult<String> purchase(@RequestBody @Valid OrderAO orderAO,
                                           HttpServletRequest request) throws IOException, ServletException {
            PojoResult<String> pojoResult = new PojoResult<>();
    }
    

    三.自定义注解校验

    框架自带的注解只能解决一些常规的校验,一些复杂逻辑的校验,可以使用自定义校验器,这是本人很喜欢的功能,重复的复杂的校验逻辑可以使用注解很优雅的代替,让代码的耦合性降低,也更简洁.
    自定义校验的基本思路是--自定义注解+自定义校验器

    自定义注解
    //这里可以加上原生的注解,直接使用非空等功能
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    //这里需要引入校验器
    @Constraint(validatedBy = CheckTagsValidator.class)
    @Documented
    public @interface CheckTags {
        String message() default "";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    
    自定义校验器
     //接口的两个泛型,分别是自定义注解和注解校验的参数类型
    public class CheckTagsValidator implements ConstraintValidator<CheckTags, List<String>> {
        @Override
        public void initialize(CheckTags constraintAnnotation) {
       }
         //方法体内可以写自己需要校验的逻辑,返回值为false则校验不通过
         @Override
         public boolean isValid(List<String> value, ConstraintValidatorContext context) {
            boolean isValid = false;
            //限定最多3个tag,最少可以0个,tag不超过4个汉字
            if (CollectionUtils.isEmpty(value)) {
                return true;
            }
            String filter = value.stream().filter(tag -> tag.length() > 4).findAny().orElse(null);
            if (StringUtils.isBlank(filter) && value.size() < 4) {
                isValid = true;
            }
            if (!isValid) {
                context.disableDefaultConstraintViolation();
                context.buildConstraintViolationWithTemplate("标签输入错误").addConstraintViolation();
            }
            return isValid;
        }
    }
    

    结合ValidatorUtil使用

    四.Controller中校验参数

    Validator本身是支持用@Valid注解自动校验的,也可可以配置校验返回策略,如下

    @Configuration
    public class ValidatorConfiguration {
        @Bean
        public Validator validator(){
            ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
                    .configure()
                    .addProperty( "hibernate.validator.fail_fast", "true" )
                    .buildValidatorFactory();
            Validator validator = validatorFactory.getValidator();
            return validator;
        }
    }
    

    个人并不推荐这种方式,参数不对时会直接抛出异常,然后@ControllerAdvice全局异常处理,返回结果
    有两个弊端,一是try/catch相对而言性能较低,二是返回异常结果不够清晰;
    推荐使用ValidatorUtil结合AOP,直接返回结果,代码如下

    @Slf4j
    @Aspect
    @Component
    public class ValidatorAspect {
    
        private static final String METHOD_POST = "POST";
    
        @Around("execution(* com.test.government.affair.controller..*(..))")
        public Object handlerControllerMethod(ProceedingJoinPoint point) throws Throwable {
            long s = System.currentTimeMillis();
            // 从获取RequestAttributes中获取HttpServletRequest的信息
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = (HttpServletRequest) requestAttributes
                    .resolveReference(RequestAttributes.REFERENCE_REQUEST);
            BucSSOUser user = SimpleUserUtil.getBucSSOUser(request);
            Object[] args = point.getArgs();
            //校验post请求参数
            log.info("Controller: 请求 URI( {} )|   user( {} )|   args:( {} )|  ", request.getRequestURI(), user.getEmpId(), args);
            if (METHOD_POST.equals(request.getMethod())) {
                for (int i = 0; i < args.length; i++) {
                    Object arg = args[i];
                    Map<String, StringBuilder> errorMap = ValidatorUtil.validate(arg);
                    if (!CollectionUtils.isEmpty(errorMap)) {
                        log.info("参数校验失败  errorMap:{}", errorMap);
                        return ServiceResult.error("000005", errorMap.toString());
                    }
                }
            }
            Object proceed = point.proceed();
            log.info("Controller: 响应 result:( {} )|   请求耗时:( {} )", proceed, System.currentTimeMillis() - s);
            return proceed;
        }
    }
    

    统一参数校验,同时打印log

    相关文章

      网友评论

          本文标题:参数校验工具之Validator自定义校验

          本文链接:https://www.haomeiwen.com/subject/jxtouqtx.html