基于Hibernate Validate的后端验证

基于AOP和Hibernate Validate框架的后台验证

整体思路
  • 对Controller层的方法做切面拦截
  • 验证入参是否使用@DoValid 注解修饰
  • 在切面类中调用Hibernate Validate框架依次对入参进行验证
  • 全部验证合法,执行原方法体,若验证不通过,动态判断返回类型,并构造返回结果
Demo
  • 新增
  • 查询
常用注解
Annotation Value Scope
@NotNull 不为null 字段或属性
@NotEmpty 不为null同时也不为空 字段或属性,String,Collection,Map,数组
@NotBlank 字符串不为null,并且不是空字符串(忽略前后空白字符) 字段或属性
@Pattern 是否匹配正则 String
... ... ...
自定义注解
  • 定义注解
  • 定义验证类
如何与系统整合
  1. pom.xml添加如下依赖:
<dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>5.3.3.Final</version>
</dependency>
  1. 在spirng.xml中添加如下配置:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">  
      <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>  
</bean> 

或者使用@Bean注解,注入该Bean

  1. 在pom.xml中引入vb依赖或者将如下文件拷贝到对应系统中
    DoValid.javaDoValidAspect.java
  /*
  * Copyright (c) 2016, www.vnetoo.com. All rights reserved.
  */
 package com.vnetoo.vcomponent.validate;

 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;

 /**
  * 参数验证注解
  * @date 2016年11月25日 下午4:48:53
  * @author zhaoj
  * @since V2.0.0
  */
 @Target({ElementType.PARAMETER})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface DoValid {
          //如需要引入分组,加入如下这句即可,代码中使用@DoValid({GroupA.class})
          //Class<?>[] value() default {};
 }
 /*
  * Copyright (c) 2016, www.vnetoo.com. All rights reserved.
  */
 package com.vnetoo.vcomponent.validate;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
 import java.util.Iterator;
 import java.util.Set;
 
 import javax.validation.ConstraintViolation;
 
 import org.apache.commons.lang.StringUtils;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Pointcut;
 import org.aspectj.lang.reflect.MethodSignature;
 import org.springframework.stereotype.Component;
 import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.servlet.ModelAndView;
 
 import com.vnetoo.common.AppContext;
 import com.vnetoo.common.enums.Result;
 import com.vnetoo.common.vo.AjaxResponse;
 import com.vnetoo.common.vo.CommonResponse;
 
 /**
  * 表单后台验证切面
  * 
  * @date 2016年11月25日 上午10:12:21
  * @author zhaoj
  * @since V2.0.0
  */
 @Aspect
 @Component
 public class DoValidAspect {
     /**
      * 验证类的结尾
      * 
      * @date 2016年11月25日 下午4:51:41
      * @author zhaoj
      * @since V2.0.0
      */
     public static final String VALIDATOR_SUFFIX = "Validator";
 
     /**
      * 定义切面
      * 
      * @date 2016年11月25日 上午10:12:02
      * @author zhaoj
      * @since V2.0.0
      */
     @Pointcut("execution(public * com.vnetoo..*Controller.*(..))")
     public void validate() {
     }
 
     /**
      * 切面方法体环绕
      * 
      * @date 2016年11月25日 上午10:11:33
      * @author zhaoj
      * @since V2.0.0
      * @param pjp
      * @return
      * @throws Throwable
      */
     @Around(value = "validate()")
     private Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
         /*
          * 算法: 1.取得方法体中的参数注解二维数组 2.逐一遍历参数中的注解,并对带有带有 @Valid 注解的参数做验证处理
          * 
          * 验证处理: 1.调用当前Controller中的validate()
          * 2.如果返回的CommonResponse.Result!=Result.Success,验证返回类型 
          * 2.1 JSON(主要用于添加、编辑):判断Controller是否有@RestController 或者 方法上加了@ResponseBody,返回带错误信息的JSON
          * 2.2 View(主要用于查询):        判断Controller是否返回值为ModelAndView,返回带错误信息的错误页面
          * 
          * 其中,错误信息在CommonResponse.msg中
          * 3.若所有验证都通过了,则可以继续执行方法
          */
         MethodSignature signature = (MethodSignature) pjp.getSignature();
         Method method = signature.getMethod();
 
         //[参数][注解]二维数组
         Annotation[][] parameterAnnotations = method.getParameterAnnotations();
 
         if (parameterAnnotations != null && parameterAnnotations.length != 0) {
             for (int i = 0; i != parameterAnnotations.length; ++i) {
                 Annotation[] param = parameterAnnotations[i];
                 for (int j = 0; j != param.length; ++j) {
                     Annotation annotation = param[j];
                     if (annotation instanceof DoValid) {
                         // 参数中有验证注解
                         Object bo = pjp.getArgs()[i];
 
                         /*******************单个入参验证开始*******************/
                         CommonResponse validator = validate(bo);
                         //分组支持
                         //CommonResponse validator = validate(bo,((DoValid) annotation).value());
                         if (validator.getResult() != Result.SUCCESS) {
                             //验证不通过,验证返回方式
                             if(isJsonResponse(pjp)){
                                 //返回JSON
                                 return new AjaxResponse(validator .getResult(), AppContext.token(), validator .getMsg());
                             }else{
                                 //返回View
                                 return new ModelAndView("errorMsg").addObject("errMsg", validator .getMsg());
                             }
                         }
                         /*******************单个入参验证结束*******************/
                     }
                 }
             }
         }
         
         //所有入参验证完毕、已做字段过滤,且均合法
         //代码若能执行到此处,则说明验证通过,则执行Controller层中的方法体
         return pjp.proceed();
     }
 
     /**
      * 判断是否是返回JSON的请求
      * @date 2016年12月1日 下午4:45:50
      * @author zhaoj
      * @since V2.0.0
      * @param pjp
      * @return
      */
     public static boolean isJsonResponse(ProceedingJoinPoint pjp){
         MethodSignature signature = (MethodSignature) pjp.getSignature();
         Method method = signature.getMethod();
         Annotation[] methodAnnotations = method.getAnnotations();
         for(Annotation tmp : methodAnnotations){
             if(tmp instanceof ResponseBody){
                 return true;
             }
         }
         
         Annotation[] classAnnotations = method.getClass().getAnnotations();
         for(Annotation tmp : classAnnotations){
             if(tmp instanceof RestController){
                 return true;
             }
         }
         
         return false;
     }
     
     /**
      * 参数验证
      * @date 2016年12月1日 下午4:46:00
      * @author zhaoj
      * @since V2.0.0
      * @param bo
      * @return
      */
     public static CommonResponse validate(Object bo) {
         Set<ConstraintViolation<Object>> set = ((LocalValidatorFactoryBean) AppContext.getBean("validator")).getValidator().validate(bo);
         //分组验证支持         
         //Set<ConstraintViolation<Object>> set2 = ((LocalValidatorFactoryBean) AppContext.getBean("validator")).getValidator().validate(bo, ((DoValid) annotation).value());
         if (set != null && !set.isEmpty()) {
             String msg = "";
 
             Iterator<ConstraintViolation<Object>> iterator = set.iterator();
             while (iterator.hasNext()) {
                 String singleMsg = iterator.next().getMessage();
                 if (StringUtils.isNotEmpty(singleMsg)) {
                     msg += singleMsg + ",";
                 }
             }
 
             if (StringUtils.isNotEmpty(msg.toString())) {
                 msg = msg.substring(0, msg.toString().length() - 1);
             }
 
             return new CommonResponse(Result.ERROR, msg);
         }
 
         return new CommonResponse(Result.SUCCESS, null);
     }
 }

如果需要指定分组的形式,请查看链接:分组验证,然后对上述代码略加修改即可。

  1. 不采用分组策略:建XXXCreateFormXXXEditFormXXXSearchForm类并在From的属性上加验证注解即可
    采用分组策略:建立分组接口,在BO的属性上加验证注解,并指定分组,详见:分组验证
  2. 在需要验证的入参中加上@DoValid注解修饰即可,如:
 /**
  * 添加保存
  * @date 2016-12-09 15:19:51
  * @author zhaoj
  * @since V1.0.3
  * @param bo
  * @return
  */
 @CheckToken
 @ResponseBody
 @Authorization
 @LogMark(memo="添加保存")
 @RequestMapping(value = "/insert")
 public AjaxResponse insert(@DoValid ExamUserCreateForm form){
     try {
         ExamUser bo = new ExamUser();
         PropertyUtils.copyProperties(bo, form);
         getService().insert(bo);
         
         return new AjaxResponse();
     } catch (Exception e) {
         return new AjaxResponse(Result.ERROR, AppContext.token(), e.getMessage());
     }
 }
参考

Hibernate Validate 中文 API
Hibernate Validate 常用注解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,860评论 18 139
  • 什么是注解(Annotation):Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和...
    九尾喵的薛定谔阅读 3,206评论 0 2
  • hibernate Validator 是 Bean Validation 的参考实现 。 Hibernate V...
    一路逆风i阅读 3,104评论 0 1
  • Java 8自Java 5(发行于2004)以来最具革命性的版本。Java 8 为Java语言、编译器、类库、开发...
    谁在烽烟彼岸阅读 907评论 0 4
  • 说谎有时候是为了保护别人、为了让对方喜欢自己、为了博取同情、为了逃避,可有的人一生爱说谎,就是喜欢说谎。开始了说谎...
    小样儿的小小样儿927阅读 1,556评论 0 0