參數(shù)校驗(yàn)工具之Validator自定義校驗(yàn)

validator百度博客一大堆,本文是摘取的部分內(nèi)容結(jié)合在項(xiàng)目中使用的經(jīng)驗(yàn).

一.導(dǎo)入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> 

二.給需要校驗(yàn)的pojo加上注解

@Data
public class InformationBasicInfoAO {
   /**
    * 主鍵id
    */
   private Long id;
   /**
    * 文章標(biāo)題
    */
   @NotBlank(message = "文章標(biāo)題不能為空")
   private String title;
   /**
    * 發(fā)布日期
    */
   @NotNull(message = "發(fā)布時(shí)間不能為空")
   private Date releaseDate;
   /**
    * 文章來源
    */
   @NotBlank(message = "文章來源不能為空")
   private String source;

message的內(nèi)容是返回給前端的提醒信息,可以不填

三.校驗(yàn)

1.通用的校驗(yàn)工具
@Slf4j
public class ValidatorUtil {
    //也可以使用spring注入的方式
    private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
    /**
     * 使用指定分組
     *
     * @param object 被校驗(yàn)的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) {
            // 這里循環(huán)獲取錯(cuò)誤信息,可以自定義格式
            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.使用工具校驗(yàn)

這種方式直接拋去異常,全局異常處理

        Map<String, StringBuilder> errorMap = ValidatorUtil.validate(object);
        if (!CollectionUtils.isEmpty(errorMap)) {
            log.info("------參數(shù)校驗(yàn)失敗:{}", errorMap);
            throw new ApplicationException(new ExceptionContext("參數(shù)校驗(yàn)失敗"), "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類,可以直接使用,直接拋出異常,需要加配置項(xiàng),具體可以百度,個(gè)人不喜歡這種方式,不夠靈活.

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

三.自定義注解校驗(yàn)

框架自帶的注解只能解決一些常規(guī)的校驗(yàn),一些復(fù)雜邏輯的校驗(yàn),可以使用自定義校驗(yàn)器,這是本人很喜歡的功能,重復(fù)的復(fù)雜的校驗(yàn)邏輯可以使用注解很優(yōu)雅的代替,讓代碼的耦合性降低,也更簡潔.
自定義校驗(yàn)的基本思路是--自定義注解+自定義校驗(yàn)器

自定義注解
//這里可以加上原生的注解,直接使用非空等功能
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
//這里需要引入校驗(yàn)器
@Constraint(validatedBy = CheckTagsValidator.class)
@Documented
public @interface CheckTags {
    String message() default "";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
自定義校驗(yàn)器
 //接口的兩個(gè)泛型,分別是自定義注解和注解校驗(yàn)的參數(shù)類型
public class CheckTagsValidator implements ConstraintValidator<CheckTags, List<String>> {
    @Override
    public void initialize(CheckTags constraintAnnotation) {
   }
     //方法體內(nèi)可以寫自己需要校驗(yàn)的邏輯,返回值為false則校驗(yàn)不通過
     @Override
     public boolean isValid(List<String> value, ConstraintValidatorContext context) {
        boolean isValid = false;
        //限定最多3個(gè)tag,最少可以0個(gè),tag不超過4個(gè)漢字
        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("標(biāo)簽輸入錯(cuò)誤").addConstraintViolation();
        }
        return isValid;
    }
}

結(jié)合ValidatorUtil使用

四.Controller中校驗(yàn)參數(shù)

Validator本身是支持用@Valid注解自動校驗(yàn)的,也可可以配置校驗(yàn)返回策略,如下

@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;
    }
}

個(gè)人并不推薦這種方式,參數(shù)不對時(shí)會直接拋出異常,然后@ControllerAdvice全局異常處理,返回結(jié)果
有兩個(gè)弊端,一是try/catch相對而言性能較低,二是返回異常結(jié)果不夠清晰;
推薦使用ValidatorUtil結(jié)合AOP,直接返回結(jié)果,代碼如下

@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();
        //校驗(yàn)post請求參數(shù)
        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("參數(shù)校驗(yàn)失敗  errorMap:{}", errorMap);
                    return ServiceResult.error("000005", errorMap.toString());
                }
            }
        }
        Object proceed = point.proceed();
        log.info("Controller: 響應(yīng) result:( {} )|   請求耗時(shí):( {} )", proceed, System.currentTimeMillis() - s);
        return proceed;
    }
}

統(tǒng)一參數(shù)校驗(yàn),同時(shí)打印log

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容