基于Hibernate Validate的后端驗證

基于AOP和Hibernate Validate框架的后臺驗證

整體思路
  • 對Controller層的方法做切面攔截
  • 驗證入?yún)⑹欠袷褂?code>@DoValid 注解修飾
  • 在切面類中調(diào)用Hibernate Validate框架依次對入?yún)⑦M行驗證
  • 全部驗證合法,執(zhí)行原方法體,若驗證不通過,動態(tài)判斷返回類型,并構(gòu)造返回結(jié)果
Demo
  • 新增
  • 查詢
常用注解
Annotation Value Scope
@NotNull 不為null 字段或?qū)傩?/td>
@NotEmpty 不為null同時也不為空 字段或?qū)傩?String,Collection,Map,數(shù)組
@NotBlank 字符串不為null,并且不是空字符串(忽略前后空白字符) 字段或?qū)傩?/td>
@Pattern 是否匹配正則 String
... ... ...
自定義注解
  • 定義注解
  • 定義驗證類
如何與系統(tǒng)整合
  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依賴或者將如下文件拷貝到對應(yīng)系統(tǒng)中
    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;

 /**
  * 參數(shù)驗證注解
  * @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 {
     /**
      * 驗證類的結(jié)尾
      * 
      * @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() {
     }
 
     /**
      * 切面方法體環(huán)繞
      * 
      * @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.取得方法體中的參數(shù)注解二維數(shù)組 2.逐一遍歷參數(shù)中的注解,并對帶有帶有 @Valid 注解的參數(shù)做驗證處理
          * 
          * 驗證處理: 1.調(diào)用當(dāng)前Controller中的validate()
          * 2.如果返回的CommonResponse.Result!=Result.Success,驗證返回類型 
          * 2.1 JSON(主要用于添加、編輯):判斷Controller是否有@RestController 或者 方法上加了@ResponseBody,返回帶錯誤信息的JSON
          * 2.2 View(主要用于查詢):        判斷Controller是否返回值為ModelAndView,返回帶錯誤信息的錯誤頁面
          * 
          * 其中,錯誤信息在CommonResponse.msg中
          * 3.若所有驗證都通過了,則可以繼續(xù)執(zhí)行方法
          */
         MethodSignature signature = (MethodSignature) pjp.getSignature();
         Method method = signature.getMethod();
 
         //[參數(shù)][注解]二維數(shù)組
         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) {
                         // 參數(shù)中有驗證注解
                         Object bo = pjp.getArgs()[i];
 
                         /*******************單個入?yún)Ⅱ炞C開始*******************/
                         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());
                             }
                         }
                         /*******************單個入?yún)Ⅱ炞C結(jié)束*******************/
                     }
                 }
             }
         }
         
         //所有入?yún)Ⅱ炞C完畢、已做字段過濾,且均合法
         //代碼若能執(zhí)行到此處,則說明驗證通過,則執(zhí)行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;
     }
     
     /**
      * 參數(shù)驗證
      * @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. 不采用分組策略:建XXXCreateForm、XXXEditForm、XXXSearchForm類并在From的屬性上加驗證注解即可
    采用分組策略:建立分組接口,在BO的屬性上加驗證注解,并指定分組,詳見:分組驗證
  2. 在需要驗證的入?yún)⒅屑由?code>@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 常用注解

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

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,569評論 19 139
  • 什么是注解(Annotation):Annotation(注解)就是Java提供了一種元程序中的元素關(guān)聯(lián)任何信息和...
    九尾喵的薛定諤閱讀 3,413評論 0 2
  • hibernate Validator 是 Bean Validation 的參考實現(xiàn) 。 Hibernate V...
    一路逆風(fēng)i閱讀 3,190評論 0 1
  • 說謊有時候是為了保護別人、為了讓對方喜歡自己、為了博取同情、為了逃避,可有的人一生愛說謊,就是喜歡說謊。開始了說謊...
    小樣兒的小小樣兒927閱讀 1,628評論 0 0
  • 云滿鳥行滅,池涼龍氣腥。斜飄看棋簟,疏灑望山亭。細響鳴林葉,圓文破沼萍。秋陰杳無際,平野但冥冥。溫庭筠的這首《秋...
    嘻錦閱讀 405評論 2 4

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