亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

五分鐘帶你了解Java的接口數(shù)據(jù)校驗

 更新時間:2020年12月10日 10:00:12   作者:谷雞泰  
這篇文章主要介紹了五分鐘帶你了解Java的接口數(shù)據(jù)校驗,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

本篇文章給大家分享平時開發(fā)中總結(jié)的一點小技巧!在工作中寫過Java程序的朋友都知道,目前使用Java開發(fā)服務(wù)最主流的方式就是通過Spring MVC定義一個Controller層接口,并將接口請求或返回參數(shù)分別定義在一個Java實體類中,這樣Spring MVC在接收到Http請求(POST/GET)后,就會自動將請求報文自動映射成一個Java對象。這樣的代碼通常是這樣寫的:

@RestController
public class OrderController {

  @Autowired
  private OrderService orderServiceImpl;

  @PostMapping("/createOrder")
  public CreateOrderBO validationTest(@Validated CreateOrderDTO createOrderDTO) {
    return orderServiceImpl.createOrder(createOrderDTO);
  }
}

這樣的代碼相信大家并不陌生,但在后續(xù)的邏輯實現(xiàn)過程中卻會遇到這樣的問題:“在接收請求參數(shù)后如何實現(xiàn)報文對象數(shù)據(jù)值的合法性校驗?”。一些同學(xué)也可能認為這并不是什么問題,因為具體某個參數(shù)字段是否為空、值的取值是否在約定范圍、格式是否合法等等,在業(yè)務(wù)代碼中校驗就好了。例如可以在Service實現(xiàn)類中對報文格式進行各種if-else的數(shù)據(jù)校驗。
從功能上說冗余的if-else代碼沒啥毛病,但從代碼的優(yōu)雅性來說冗長的if-else代碼會顯得非常臃腫。接下來的內(nèi)容將給大家介紹一種處理此類問題的實用方法。具體將從以下幾個方面進行介紹:

  • 使用@Validated注解實現(xiàn)Controller接口層數(shù)據(jù)直接綁定校驗;
  • 擴展約束性注解實現(xiàn)數(shù)據(jù)取值范圍的校驗;
  • 更加靈活的對象數(shù)據(jù)合法性校驗工具類封裝;
  • 數(shù)據(jù)合法性校驗結(jié)果異常統(tǒng)一返回處理;

Controller接口層數(shù)據(jù)綁定校驗

實際上在Java開發(fā)中目前普通使用的Bean數(shù)據(jù)校驗工具是"hibernate-validator",它是一個hibernete獨立的jar包,所以使用這個jar包并不需要一定要集成Hibernete框架。該jar包主要實現(xiàn)并擴展了javax.validation(是一個基于JSR-303標準開發(fā)出來的Bean校驗規(guī)范)接口。

由于Spring Boot在內(nèi)部默認集成了"hibernate-validator",所以使用Spring Boot構(gòu)建的Java工程可以直接使用相關(guān)注解來實現(xiàn)Bean的數(shù)據(jù)校驗。例如我們最常編寫的Controller層接口參數(shù)對象,可以在定義Bean類時直接編寫這樣的代碼:

@Data
public class CreateOrderDTO {

  @NotNull(message = "訂單號不能為空")
  private String orderId;
  @NotNull(message = "訂單金額不能為空")
  @Min(value = 1, message = "訂單金額不能小于0")
  private Integer amount;
  @Pattern(regexp = "^1[3|4|5|7|8][0-9]{9}$", message = "用戶手機號不合法")
  private String mobileNo;
  private String orderType;
  private String status;
}

如上所示代碼,我們可以使用@NotNull注解來約束該字段必須不能為空,也可以使用@Min注解來約束字段的最小取值,或者還可以通過@Pattern注解來使用正則表達式來約束字段的格式(如手機號格式)等等。

以上這些注解都是“hibernate-validator”依賴包默認提供的,更多常用的注解還有很多,例如:

利用這些約束注解,我們就可以很輕松的搞定接口數(shù)據(jù)校驗,而不需要在業(yè)務(wù)邏輯中編寫大量的if-else來進行數(shù)據(jù)合法性校驗。而定義好Bean參數(shù)對象并使用相關(guān)注解實現(xiàn)參數(shù)值約束后,在Controller層接口定義中只需要使用@Validated注解就可以實現(xiàn)在接收參數(shù)后自動進行數(shù)據(jù)綁定校驗了,具體代碼如下:

@PostMapping("/createOrder")
public CreateOrderBO validationTest(@Validated CreateOrderDTO createOrderDTO) {
  return orderServiceImpl.createOrder(createOrderDTO);
}

如上所示,在Controller層中通過Spring提供的@Validated注解可以自動實現(xiàn)數(shù)據(jù)Bean的綁定校驗,如果數(shù)據(jù)異常則會統(tǒng)一拋出校驗異常!

約束性注解擴展

在“hibernate-validator”依賴jar包中,雖然提供了很多很方便的約束注解,但是也有不滿足某些實際需要的情況,例如我們想針對參數(shù)中的某個值約定其值的枚舉范圍,如orderType訂單類型只允許傳“pay”、“refund”兩種值,那么現(xiàn)有的約束注解可能就沒有特別適用的了。此外,如果對這樣的枚舉值,我們還想在約束定義中直接匹配代碼中的枚舉定義,以更好地統(tǒng)一接口參數(shù)與業(yè)務(wù)邏輯的枚舉定義。那么這種情況下,我們還可以自己擴展定義相應(yīng)地約束注解邏輯。
接下來我們定義新的約束注解@EnumValue,來實現(xiàn)上面我們所說的效果,具體代碼如下:

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {EnumValueValidator.class})
public @interface EnumValue {

  //默認錯誤消息
  String message() default "必須為指定值";

  //支持string數(shù)組驗證
  String[] strValues() default {};

  //支持int數(shù)組驗證
  int[] intValues() default {};

  //支持枚舉列表驗證
  Class<?>[] enumValues() default {};

  //分組
  Class<?>[] groups() default {};

  //負載
  Class<? extends Payload>[] payload() default {};

  //指定多個時使用
  @Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
  @Retention(RUNTIME)
  @Documented
  @interface List {
    EnumValue[] value();
  }

  /**
   * 校驗類邏輯定義
   */
  class EnumValueValidator implements ConstraintValidator<EnumValue, Object> {

    //字符串類型數(shù)組
    private String[] strValues;
    //int類型數(shù)組
    private int[] intValues;
    //枚舉類
    private Class<?>[] enumValues;

    /**
     * 初始化方法
     *
     * @param constraintAnnotation
     */
    @Override
    public void initialize(EnumValue constraintAnnotation) {
      strValues = constraintAnnotation.strValues();
      intValues = constraintAnnotation.intValues();
      enumValues = constraintAnnotation.enumValues();
    }

    /**
     * 校驗方法
     *
     * @param value
     * @param context
     * @return
     */
    @SneakyThrows
    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
      //針對字符串數(shù)組的校驗匹配
      if (strValues != null && strValues.length > 0) {
        if (value instanceof String) {
          for (String s : strValues) {//判斷值類型是否為Integer類型
            if (s.equals(value)) {
              return true;
            }
          }
        }
      }
      //針對整型數(shù)組的校驗匹配
      if (intValues != null && intValues.length > 0) {
        if (value instanceof Integer) {//判斷值類型是否為Integer類型
          for (Integer s : intValues) {
            if (s == value) {
              return true;
            }
          }
        }
      }
      //針對枚舉類型的校驗匹配
      if (enumValues != null && enumValues.length > 0) {
        for (Class<?> cl : enumValues) {
          if (cl.isEnum()) {
            //枚舉類驗證
            Object[] objs = cl.getEnumConstants();
            //這里需要注意,定義枚舉時,枚舉值名稱統(tǒng)一用value表示
            Method method = cl.getMethod("getValue");
            for (Object obj : objs) {
              Object code = method.invoke(obj, null);
              if (value.equals(code.toString())) {
                return true;
              }
            }
          }
        }
      }
      return false;
    }
  }
}

如上所示的@EnumValue約束注解,是一個非常實用的擴展,通過該注解我們可以實現(xiàn)對參數(shù)取值范圍(不是大小范圍)的約束,它支持對int、string以及enum三種數(shù)據(jù)類型的約束,具體使用方式如下:

/**
 * 定制化注解,支持參數(shù)值與指定類型數(shù)組列表值進行匹配(缺點是需要將枚舉值寫死在字段定義的注解中)
 */
@EnumValue(strValues = {"pay", "refund"}, message = "訂單類型錯誤")
private String orderType;
/**
 * 定制化注解,實現(xiàn)參數(shù)值與枚舉列表的自動匹配校驗(能更好地與實際業(yè)務(wù)開發(fā)匹配)
 */
@EnumValue(enumValues = Status.class, message = "狀態(tài)值不在指定范圍")
private String status;

如上所示代碼,該擴展注解既可以使用strValues或intValues屬性來編程列舉取值范圍,也可以直接通過enumValues來綁定枚舉定義。但是需要注意,處于通用考慮,具體枚舉定義的屬性的名稱要統(tǒng)一匹配為value、desc,例如Status枚舉定義如下:

public enum Status {
  PROCESSING(1, "處理中"),
  SUCCESS(2, "訂單已完成");
  Integer value;
  String desc;

  Status(Integer value, String desc) {
    this.value = value;
    this.desc = desc;
  }

  public Integer getValue() {
    return value;
  }

  public String getDesc() {
    return desc;
  }
}

通過注解擴展,就能實現(xiàn)更多方便的約束性注解!

更加靈活的數(shù)據(jù)校驗工具類封裝

除了上面直接在Controller層使用@Validated進行綁定數(shù)據(jù)校驗外,在有些情況,例如你的參數(shù)對象中的某個字段是一個復(fù)合對象,或者業(yè)務(wù)層的某個方法所定義的入?yún)ο笠残枰M行數(shù)據(jù)合法性校驗,那么這種情況下如何實現(xiàn)像Controller層一樣的校驗效果呢?

需要說明在這種情況下@Validated已經(jīng)無法直接使用了,因為@Validated注解發(fā)揮作用主要是Spring MVC在接收參數(shù)的過程中實現(xiàn)了自動數(shù)據(jù)綁定校驗,而在普通的業(yè)務(wù)方法或者復(fù)合參數(shù)對象中是沒有辦法直接綁定校驗的。這種情況下,我們可以通過定義ValidateUtils工具類來實現(xiàn)一樣的校驗效果,具體代碼如下:

public class ValidatorUtils {

  private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

  /**
   * bean整體校驗,有不合規(guī)范,拋出第1個違規(guī)異常
   */
  public static void validate(Object obj, Class<?>... groups) {
    Set<ConstraintViolation<Object>> resultSet = validator.validate(obj, groups);
    if (resultSet.size() > 0) {
      //如果存在錯誤結(jié)果,則將其解析并進行拼湊后異常拋出
      List<String> errorMessageList = resultSet.stream().map(o -> o.getMessage()).collect(Collectors.toList());
      StringBuilder errorMessage = new StringBuilder();
      errorMessageList.stream().forEach(o -> errorMessage.append(o + ";"));
      throw new IllegalArgumentException(errorMessage.toString());
    }
  }
}

如上所示,我們定義了一個基于"javax.validation"接口的工具類實現(xiàn),這樣就可以在非@Validated直接綁定校驗的場景中通過校驗工具類來實現(xiàn)對Bean對象約束注解的校驗處理,具體使用代碼如下:

public boolean orderCheck(OrderCheckBO orderCheckBO) {
  //對參數(shù)對象進行數(shù)據(jù)校驗
  ValidatorUtils.validate(orderCheckBO);
  return true;
}

而方法入?yún)ο髣t還是可以繼續(xù)使用前面我們介紹的約束性注解進行約定,例如上述方法的入?yún)ο蠖x如下:

@Data
@Builder
public class OrderCheckBO {

  @NotNull(message = "訂單號不能為空")
  private String orderId;
  @Min(value = 1, message = "訂單金額不能小于0")
  private Integer orderAmount;
  @NotNull(message = "創(chuàng)建人不能為空")
  private String operator;
  @NotNull(message = "操作時間不能為空")
  private String operatorTime;
}

這樣在編程體驗上就可以整體上保持一致!

數(shù)據(jù)合法性校驗結(jié)果異常統(tǒng)一處理

通過前面我們所講的各種約束注解,我們實現(xiàn)了對Controller層接口以及業(yè)務(wù)方法參數(shù)對象的統(tǒng)一數(shù)據(jù)校驗。而為了保持校驗異常處理的統(tǒng)一處理和錯誤報文統(tǒng)一輸出,我們還可以定義通用的異常處理機制,來保證各類數(shù)據(jù)校驗錯誤都能以統(tǒng)一錯誤格式反饋給調(diào)用方。具體代碼如下:

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
  /**
   * 統(tǒng)一處理參數(shù)校驗錯誤異常(非Spring接口數(shù)據(jù)綁定驗證)
   *
   * @param response
   * @param e
   * @return
   */
  @ExceptionHandler(BindException.class)
  @ResponseBody
  public ResponseResult<?> processValidException(HttpServletResponse response, BindException e) {
    response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
    //獲取校驗錯誤結(jié)果信息,并將信息組裝
    List<String> errorStringList = e.getBindingResult().getAllErrors()
        .stream().map(ObjectError::getDefaultMessage).collect(Collectors.toList());
    String errorMessage = String.join("; ", errorStringList);
    response.setContentType("application/json;charset=UTF-8");
    log.error(e.toString() + "_" + e.getMessage(), e);
    return ResponseResult.systemException(GlobalCodeEnum.GL_FAIL_9998.getCode(),
        errorMessage);
  }

  /**
   * 統(tǒng)一處理參數(shù)校驗錯誤異常
   *
   * @param response
   * @param e
   * @return
   */
  @ExceptionHandler(IllegalArgumentException.class)
  @ResponseBody
  public ResponseResult<?> processValidException(HttpServletResponse response, IllegalArgumentException e) {
    response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
    String errorMessage = String.join("; ", e.getMessage());
    response.setContentType("application/json;charset=UTF-8");
    log.error(e.toString() + "_" + e.getMessage(), e);
    return ResponseResult.systemException(GlobalCodeEnum.GL_FAIL_9998.getCode(),
        errorMessage);
  }

  ...
}

如上所示,我們定義了針對前面兩種數(shù)據(jù)校驗方式的統(tǒng)一異常處理機制,這樣數(shù)據(jù)校驗的錯誤信息就能通過統(tǒng)一的報文格式反饋給調(diào)用端,從而實現(xiàn)接口數(shù)據(jù)報文的統(tǒng)一返回!

其中通用的接口參數(shù)對象ResponseResult的代碼定義如下:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonPropertyOrder({"code", "message", "data"})
public class ResponseResult<T> implements Serializable {

  private static final long serialVersionUID = 1L;

  /**
   * 返回的對象
   */
  @JsonInclude(JsonInclude.Include.NON_NULL)
  private T data;
  /**
   * 返回的編碼
   */
  private Integer code;
  /**
   * 返回的信息
   */
  private String message;

  /**
   * @param data 返回的數(shù)據(jù)
   * @param <T> 返回的數(shù)據(jù)類型
   * @return 響應(yīng)結(jié)果
   */
  public static <T> ResponseResult<T> OK(T data) {
    return packageObject(data, GlobalCodeEnum.GL_SUCC_0);
  }

  /**
   * 自定義系統(tǒng)異常信息
   *
   * @param code
   * @param message 自定義消息
   * @param <T>
   * @return
   */
  public static <T> ResponseResult<T> systemException(Integer code, String message) {
    return packageObject(null, code, message);
  }
}

當然,這樣的統(tǒng)一報文格式也不僅僅只處理異常返回,正常的數(shù)據(jù)報文格式也可以通過該對象來進行統(tǒng)一封裝!
本文內(nèi)容從實用的角度給大家演示了,如何在日常工作中編寫通用的數(shù)據(jù)校驗邏輯,希望能對大家有所幫助!

 到此這篇關(guān)于五分鐘帶你了解Java的接口數(shù)據(jù)校驗的文章就介紹到這了,更多相關(guān)Java 接口數(shù)據(jù)校驗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java子類實例化總是默認調(diào)用父類的無參構(gòu)造操作

    Java子類實例化總是默認調(diào)用父類的無參構(gòu)造操作

    這篇文章主要介紹了Java子類實例化總是默認調(diào)用父類的無參構(gòu)造操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • SpringBoot整合Drools規(guī)則引擎動態(tài)生成業(yè)務(wù)規(guī)則的實現(xiàn)

    SpringBoot整合Drools規(guī)則引擎動態(tài)生成業(yè)務(wù)規(guī)則的實現(xiàn)

    本文主要介紹了SpringBoot整合Drools規(guī)則引擎動態(tài)生成業(yè)務(wù)規(guī)則的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Java如何實現(xiàn)內(nèi)存緩存

    Java如何實現(xiàn)內(nèi)存緩存

    內(nèi)存緩存(Memory?caching)是一種常見的緩存技術(shù),它利用計算機的內(nèi)存存儲臨時數(shù)據(jù),以提高數(shù)據(jù)的讀取和訪問速度,本文就來和大家聊聊Java如何實現(xiàn)內(nèi)存緩存吧
    2023-08-08
  • Spring?MVC?前端控制器?(DispatcherServlet)處理流程解析

    Spring?MVC?前端控制器?(DispatcherServlet)處理流程解析

    DispatcherServlet是前置控制器,配置在web.xml文件中的,這篇文章主要介紹了Spring?MVC?前端控制器?(DispatcherServlet)處理流程,需要的朋友可以參考下
    2022-05-05
  • mybatis-plus主鍵生成策略

    mybatis-plus主鍵生成策略

    這篇文章主要介紹了mybatis-plus主鍵生成策略,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Spring將一個的類配置成Bean的方式詳解

    Spring將一個的類配置成Bean的方式詳解

    這篇文章主要介紹了Spring將一個的類配置成Bean的方式,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-03-03
  • 解決IDEA service層跳轉(zhuǎn)實現(xiàn)類的快捷圖標消失問題

    解決IDEA service層跳轉(zhuǎn)實現(xiàn)類的快捷圖標消失問題

    這篇文章主要介紹了解決IDEA service層跳轉(zhuǎn)實現(xiàn)類的快捷圖標消失問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • java中json和對象之間相互轉(zhuǎn)換的運用

    java中json和對象之間相互轉(zhuǎn)換的運用

    本文主要介紹了java中json和對象之間相互轉(zhuǎn)換的運用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Java實現(xiàn)excel動態(tài)列導(dǎo)出的示例代碼

    Java實現(xiàn)excel動態(tài)列導(dǎo)出的示例代碼

    這篇文章主要為大家詳細介紹了如何使用Java實現(xiàn)excel動態(tài)列導(dǎo)出,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • Spring操作JdbcTemplate數(shù)據(jù)庫的方法學(xué)習(xí)

    Spring操作JdbcTemplate數(shù)據(jù)庫的方法學(xué)習(xí)

    這篇文章主要為大家介紹了Spring操作JdbcTemplate數(shù)據(jù)庫方法學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05

最新評論