美文网首页SSM+shiro等
基于Hibernate Validate的后端验证

基于Hibernate Validate的后端验证

作者: 小箭子 | 来源:发表于2016-12-06 14:54 被阅读215次

    基于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 常用注解

    相关文章

      网友评论

        本文标题:基于Hibernate Validate的后端验证

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