HibernateValidator自定義驗證

前言

在本身的摸索下對HibernateValidator有了初步的認識,能夠使用已有的約束條件對字段作出限制,減小不要代碼的出現,使代碼更簡潔。
但在最近的實際使用中,出現了一些沒法使用框架處理的問題,例如,在第三方請求個人接口時,根據status字段區分不一樣的業務邏輯;status=1進行A邏輯處理,status=2進行B邏輯處理;在網絡了檢索了相關信息後,作出以下總結。java

步驟

  1. 使用通用Mapper插件生成實體類和mapper,這裏爲了聚焦對HibernateValidator的使用,把關注點放在實體類Brand中,在實體類中,對status字段添加自定義約束@StatusConstraintweb

    package com.codeup.mybatisjoin.model;
    
    import com.codeup.mybatisjoin.validation.StatusConstraint;
    import lombok.Data;
    import javax.persistence.Column;
    import javax.persistence.Id;
    import javax.validation.constraints.NotBlank;
    import javax.validation.constraints.NotNull;
    
    @Data
    public class Brand {
       
        @Id
        @Column(name = "brand_ID")
        private Long brandId;
    
    
        /**
         * 使用`@NotNull`添加約束
         */
        @Column(name = "vendor_ID")
        @NotNull(message = "[vendorId]不可爲空")
        private Long vendorId;
    
    
        /**
         * 使用`@NotBlank`添加約束
         */
        @Column(name = "brand_name")
        @NotBlank(message = "[brandName]字段不可爲空")
        private String brandName;
    
        private String description;
    
    
        /**
         * 自定義約束
         */
        @StatusConstraint(message = "[status]爲1或者2")
        private String status;
    }
  2. @StatusConstraint實現步驟和自定義註解類似spring

    package com.codeup.mybatisjoin.validation;
    
    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @ProjectName: mybatisjoin
     * @Package: com.codeup.mybatisjoin.validation
     * @ClassName: StatusConstraint
     * @Author: lhc
     * @Description: 狀態約束
     * @Date: 2019/8/22 下午 3:36
     */
    @Target({ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    /**
     * 指出當前約束是經過`StatusConstraintValidator.class`來實現的
     */
    @Constraint(validatedBy = StatusConstraintValidator.class)
    public @interface StatusConstraint {
    
        /**
         * 配置message信息
         * @return
         */
        String message() default "違規參數";
    
        /**
         * 分組
         * @return
         */
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    }
  3. 經過查看源代碼裏面的文檔得知要要實現ConstraintValidator接口json

    @Documented
    @Target({ ANNOTATION_TYPE })
    @Retention(RUNTIME)
    public @interface Constraint {
    
        /**
         * {@link ConstraintValidator} classes implementing the constraint. The given 
    classes
         * must reference distinct target types for a given {@link ValidationTarget}. 
    If two
         * {@code ConstraintValidator}s refer to the same type, an exception will 
    occur.
         * <p>
         * At most one {@code ConstraintValidator} targeting the array of parameters of
         * methods or constructors (aka cross-parameter) is accepted. If two or more
         * are present, an exception will occur.
         *
         * @return array of {@code ConstraintValidator} classes implementing the 
    constraint
         */
        Class<? extends ConstraintValidator<?, ?>>[] validatedBy();
    }
  4. 實現ConstraintValidator接口,重寫isValid()方法,實現本身的判斷邏輯網絡

    package com.codeup.mybatisjoin.validation;
    
    import lombok.extern.slf4j.Slf4j;
    
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    
    /**
     * @ProjectName: mybatisjoin
     * @Package: com.codeup.mybatisjoin.validation
     * @ClassName: StatusConstraintValidator
     * @Author: lhc
     * @Description: TODO
     * @Date: 2019/8/22 下午 3:38
     */
    @Slf4j
    public class StatusConstraintValidator implements 
    ConstraintValidator<StatusConstraint,Object> {
    
        @Override
        public boolean isValid(Object value, ConstraintValidatorContext context) {
            String statCode = (String) value;
            // 等於1或2返回true,反之
            if ("1".equals(statCode) || "2".equals(statCode)) {
                return true;
            }
            return false;
        }
    }
  5. 至此,對字段添加約束條件已完成。還存在一個重要的操做是對約束的校驗,這裏經過工具類實現mybatis

    package com.codeup.mybatisjoin.validation;
    
    import org.hibernate.validator.HibernateValidator;
    
    import javax.validation.ConstraintViolation;
    import javax.validation.Validation;
    import javax.validation.Validator;
    import java.util.Set;
    
    /**
     * @ProjectName: mybatisjoin
     * @Package: com.codeup.mybatisjoin.validation
     * @ClassName: ValidatorConfig
     * @Author: lhc
     * @Description: TODO
     * @Date: 2019/8/22 下午 4:31
     */
    public class ValidatorUtil {
    
    
        /**
         * 配置hibernate_validator和快速失敗模式
         */
        private static Validator validator = 
    Validation.byProvider(HibernateValidator.class)
               .configure()
               .failFast(true)
               .buildValidatorFactory()
               .getValidator();
    
    
        /**
         * 參數校驗,若未匹配約束,則經過已將將以前定義的`message`拋出
         * @param object 參數
         * @param groups 屬於組
         */
        public static void result(Object object, Class<?>... groups) {
            Set<ConstraintViolation<Object>> constraintViolations = 
    validator.validate(object, groups);
             if (constraintViolations.size() > 0) {
                String message = constraintViolations.iterator().next().getMessage();
                throw new MissingParameterException(message);
            }
        }
    }
  6. 將message信息拋出以後,須要以更統一的方式返回給第三方,使用統一異常處理機制解決app

    • 自定義異常類MissingParameterException框架

      package com.codeup.mybatisjoin.validation;
      
      public class MissingParameterException extends RuntimeException {
      
          public MissingParameterException(String message) {
              super(message);
          }
      }
    • 統一異常處理ide

      package com.codeup.mybatisjoin.validation;
      
      import org.springframework.web.bind.annotation.ExceptionHandler;
      import org.springframework.web.bind.annotation.RestControllerAdvice;
      
      import java.util.HashMap;
      import java.util.Map;
      
      
      @RestControllerAdvice
      public class GlobalExceptionHandler {
      
          @ExceptionHandler(MissingParameterException.class)
          public Map<String, Object> invalidParameter(MissingParameterException e) {
              Map<String, Object> map = new HashMap<>();
              map.put("code", 500);
              map.put("message", e.getMessage());
              return map;
          }
      }
  • 經過請求查看返回結果工具

    package com.codeup.mybatisjoin.controller;
    
    import com.codeup.mybatisjoin.model.Brand;
    import com.codeup.mybatisjoin.validation.ValidatorUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    
    @Slf4j
    @RestController
    @RequestMapping(value = "/brandConroller")
    public class BrandConroller {
    
        @PostMapping(value = "/go")
        public Map<String, Object> go(@RequestBody Brand brand) {
            ValidatorUtil.result(brand);
            Map<String, Object> map = new LinkedHashMap<>();
            log.info("brand:{}", brand);
            return map;
        }
    }
  1. 請求

    // request
     {
        "brandId": 1,
        "vendorId": 1,
        "brandName": "demoData",
        "description": "demoData",
           "status": "3"
    }
    // response
    {
        "code": 500,
        "message": "[status]爲1或者2"
    }

若有不妥之處,請吐槽我

相關文章
相關標籤/搜索