參數校驗工具之Validator自定義校驗

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的內容是返回給前端的提醒信息,能夠不填java

三.校驗

1.通用的校驗工具spring

@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.使用工具校驗api

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

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類,能夠直接使用,直接拋出異常,須要加配置項,具體能夠百度,我的不喜歡這種方式,不夠靈活.maven

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

三.自定義註解校驗

框架自帶的註解只能解決一些常規的校驗,一些複雜邏輯的校驗,能夠使用自定義校驗器,這是本人很喜歡的功能,重複的複雜的校驗邏輯能夠使用註解很優雅的代替,讓代碼的耦合性下降,也更簡潔.
自定義校驗的基本思路是--自定義註解+自定義校驗器ide

自定義註解工具

//這裏能夠加上原生的註解,直接使用非空等功能
@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 {};
}

自定義校驗器post

//接口的兩個泛型,分別是自定義註解和註解校驗的參數類型
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 做者:從入門到脫髮 連接:https://www.jianshu.com/p/b3876bf9396c 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

相關文章
相關標籤/搜索