1. 程式人生 > 程式設計 >SpringBoot中的異常處理與引數校驗的方法實現

SpringBoot中的異常處理與引數校驗的方法實現

兄弟們好,這次來跟老鐵交流兩個問題,異常和引數校驗,在說引數校驗之前我們先來說異常處理吧,因為後面引數的校驗會牽扯到異常處理這塊的內容。

異常處理

說到異常處理,我不知道大家有沒有寫過或者遇到過如下的寫法。

public void saveUser() {
    
  try {
    // 所有的業務內容,目測幾百行
  }catch (Exception e) {
    e.printStackTrace();
  }
}

如果出現上述的程式碼,裡面包含了大量的業務程式碼,如果是你寫的,趕緊改掉,不是你寫的找寫的,吐槽趕緊改掉。

存在的問題:

1、會遇到效能瓶頸;
2、很難定位問題;

3、try巢狀過多可讀性很差;

不管什麼原因出現了上述程式碼,那麼最好還是改一下,如果非要在業務程式碼中try,那麼也應該只在可能出現異常的地方使用try,而不是try整個業務程式碼。

SpringBoot中的異常捕獲

直接上程式碼

@RestControllerAdvice
public class GlobalException {

  @ExceptionHandler(value = Exception.class) // 捕獲的異常型別
  public Object globalException(Exception ex) {

    // 異常處理
    ex.printStackTrace();

    return "出現異常";
  }
}

那麼在SpringBoot中我們就可以通過這樣的一個配置可以獲取到專案中出現異常的地方,我們可以在這個方法中可以獲取出現異常的類的詳細資訊,那麼是不是所有的異常我們全部使用Exception來處理呢?那麼肯定是不合適的。
我們模擬一個by zero的異常,然後再配置一個處理ArithmeticException異常的處理器,程式碼如下:

@RestControllerAdvice
public class GlobalException {


  @ExceptionHandler(value = Exception.class) // 捕獲的異常型別
  public Object globalException(Exception ex) {

    ex.printStackTrace();

    return "出現異常";
  }

  @ExceptionHandler(value = ArithmeticException.class)
  public Object arithmeticException(ArithmeticException ex) {

    ex.printStackTrace();
    return "by zero異常";
  }
}

如果這個時候出現by zero異常,走ArithmeticException異常處理,原因就是因為如果有更小範圍的異常處理類,那麼會走小範圍的異常處理器。不會走globalException更大的異常處理類。

這樣處理之後,我們就不需要在專案中去寫那麼多的try了,是不是方便了很多。

除了使用這些已經存在的異常外,其實我們還可以自定義我們的異常,比如我們常用的使用者未登入異常、引數錯誤異常等等。但是考慮到這篇文章的篇幅問題,這次就先不寫了,有興趣的朋友可以直接下面留言,人多了我儘快更新。

注意坑:

這裡跟大家分享一個踩過的坑,不能再Filter過濾器中丟擲異常,如果通過在過濾器中丟擲異常,然後通過異常處理類來處理,那麼是不可能的,因為處理器是捕獲不到Filter丟擲的異常的。

引數校驗

老規矩,先來看一段程式碼

@RequestMapping(value = "/save/user")
public Object saveUser(UserPO userPO) {

  if (userPO.getAge() == null) {
    return "請求引數錯誤";
  }

  if (userPO.getSex() == null) {
    return "請求引數錯誤";
  }
  if (userPO.getUsername() == null) {
    return "請求引數錯誤";
  }

  // ...

  return "SUCCESS";
}

應該見過這種校驗引數的吧,說實話我寫過。越寫感覺越low,所以狠心一下,還是趁早改吧。

@Validated註解

這個註解其實是Spring提供的,如果你的專案不是SpringBoot專案,需要引一下需要的pom檔案,如果是,那麼就不用管了,SpringBoot已經幫我們引入了。

網上看了好多的部落格,許多都說的不是很全,大部分都是說JavaBean引數的校驗,但是我們專案中有些介面可能就涉及一個引數,根本不需要寫一個JavaBean,對於單一引數的校驗好多部落格還是沒說的,那麼我們這次就一次性講清楚。

單一引數的校驗

直接看程式碼吧

@Validated
@RestController
public class BookController {
  
  @RequestMapping(value = "/book/info",method = RequestMethod.GET)
  public Object getBookInfo(@NotBlank(message = "書籍ID不能為空") String bookId) {

    return "SUCCESS";
  }
}

這裡要跟大家特別說明下,如果是單一引數的校驗,那麼我們必須要在類上面新增@Validated註解,不然我們整個單個引數校驗是不會生效的,可以看到我們在校驗引數bookId的時候,使用了@NotBlank那麼顧名思義,就是這個引數不能為null,在呼叫了trim()方法之後也不能是空字元。

如果引數不滿足要求,那麼會丟擲ConstraintViolationException異常,這個異常只有在單一引數校驗的時候丟擲,如果你的引數是JavaBean,那麼就不是這個異常了。

既然我們知道了它會丟擲異常,並且我們也知道是什麼異常型別,那麼久超級簡單了,我們可以直接使用上面剛學的異常處理類來處理我們的異常。

我找個裡面寫的比較簡單,如果你想寫的複雜一點,其實也是可以的,但是作為後端來說,我覺得沒必要,因為我們不能給前端提示太過明顯的錯誤提示,防止別人惡意攻擊我們,就像使用者名稱密碼錯誤,不能明確的告訴使用者到底是使用者名稱錯誤還是密碼錯誤,只能提示使用者名稱或密碼錯誤。

如果大家非要把詳細的錯誤資訊打出來,要看到到底是哪個引數校驗不通過,也可以通過下面的方式將具體的引數錯誤資訊打印出來。輸出的錯誤結果其實就是上面message裡面的內容。

@RestControllerAdvice
public class ExceptionCatch {
  /**
   * 單個引數異常處理
   *
   * @param ex
   * @return
   */
  @ExceptionHandler(value = ConstraintViolationException.class)
  public Object constraintViolationException(ConstraintViolationException ex) {

    // 獲取具體的錯誤資訊
    Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
    // 列印資料
    violations.forEach(e -> System.out.println(e.getMessage()));
    
    return "單個-請求引數錯誤";
  }
}

JavaBean引數校驗(form-data)

JavaBean的寫法

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserPO {

  @NotBlank(message = "使用者名稱不能為空")
  private String username;

  @NotNull(message = "年齡不能為空")
  @Min(value = 1,message = "年齡最小為1")
  @Max(value = 200,message = "年齡最大為200")
  private Integer age;

  @NotBlank(message = "性別不能為空")
  private String sex;
}

Controller寫法

@RequestMapping(value = "/save/user")
public Object saveUser(@Validated UserPO userPO) {

  // ...

  return "SUCCESS";
}

跟單一引數校驗不一樣的是JavaBean的校驗方式需要將@Validated寫在方法引數,而不是類上。如果出現了引數校驗不通過,同樣的也會丟擲一個異常,BindException。

/**
 * 一般引數校驗繫結異常處理
 *
 * @param ex
 * @return
 */
@ExceptionHandler(value = BindException.class)
public Object bindException(BindException ex) {

  BindingResult bindingResult = ex.getBindingResult();

  // 獲取所有的錯誤資訊
  List<ObjectError> allErrors = bindingResult.getAllErrors();

  // 輸出
  allErrors.forEach(e -> System.out.println(e.getDefaultMessage()));

  return "請求引數錯誤";
}

注意:大家要注意post請求有兩種方式,一種是基於form-data格式的資料傳遞,另外一種就是基於json格式的資料傳遞,兩種傳遞方式引發的異常也是不一樣的,所以我們還要單獨處理基於json的引數校驗異常處理。

JavaBean引數校驗(json)

我們先來看下Controller接收方式

@RequestMapping(value = "/save/user")
public Object saveUser(@Validated @RequestBody UserPO userPO) {

  // ...

  return "SUCCESS";
}

對應的引數異常處理

/**
 * JSON引數校驗繫結異常處理
 *
 * @param ex
 * @return
 */
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Object methodArgumentNotValidException(MethodArgumentNotValidException ex) {

  BindingResult bindingResult = ex.getBindingResult();

  // 獲取所有的錯誤資訊
  List<ObjectError> allErrors = bindingResult.getAllErrors();

  // 輸出
  allErrors.forEach(e -> System.out.println(e.getDefaultMessage()));

  return "請求引數錯誤-json";
}

最後的話

那麼到這裡,我們本篇文章就結束了,主要介紹了兩部分內容,異常的處理和引數的校驗。雖然很簡單,但是我個人感覺還是挺常用的技能。所以與大家進行分享,如果對你有點幫助,就來點個贊吧。如果有什麼不明白的也歡迎下方留言,一起來交流。

到此這篇關於SpringBoot中的異常處理與引數校驗的方法實現的文章就介紹到這了,更多相關SpringBoot 異常處理與引數校驗內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!