1. 程式人生 > >Spring 異常處理之本地處理

Spring 異常處理之本地處理

承接上文Spring 異常處理之 HTTP 狀態碼,本文介紹spring異常處理的第二種方式。為什麼說這種異常處理方式叫本地處理,因為該方式是在Controller內部處理該Controller中的方法丟擲的異常。

需要注意的是,這種方式其實還可以細分為3種。具體見程式碼:

  • 注意,下面的方法在同一個controller中
/**
  * 異常處理之 ExceptionHandler 之一
  *
  * 這異常沒有被 ResponseStatus 標註,必須被 handler 捕捉
  *
  * 本類的 conflict 方法捕獲了它,對比下面一對
  *
  * @return 什麼也不返回,總是丟擲異常
  */
 @RequestMapping("/dataIntegrityViolation")
 public String throwDataIntegrityViolationException() {
     throw new DataIntegrityViolationException("ID 重複");
 }

 /**
  * 異常處理之使用 ExceptionHandler | 這個方法什麼都不做,沒什麼實際應用價值
  *
  * 但是它可以為前端提供豐富的異常資訊
  */
 @ResponseStatus(value = HttpStatus.CONFLICT, reason = "資料衝突")
 @ExceptionHandler(DataIntegrityViolationException.class)
 public void conflict() {
     // 什麼也不做
 }

 /**
  * 異常處理之 ExceptionHandler 之二
  *
  * 這異常沒有被 ResponseStatus 標註,必須被 handler 捕捉
  *
  * 本類中的 databaseError 方法捕獲了它,同時,該方法指定了 view,對比上面那一對
  *
  * @return 什麼也不返回,總是丟擲異常
  * @throws SQLException 總是丟擲這個異常
  */
 @RequestMapping("/databaseError")
 public String throwSqlException() throws SQLException {
     throw new SQLException("SQL 異常");
 }

 /**
  * 異常處理之使用 ExceptionHandler | 指定了 view
  *
  * 但是這種方式幾乎不能給前端提供有效的異常資訊
  *
  * @param exception 異常物件
  * @return 檢視名稱
  */
 @ExceptionHandler({SQLException.class})
 public String databaseError(Exception exception) {
     logger.info(exception.toString());
     return "database.error";
 }

 /**
  * 異常處理之 ExceptionHandler 之三
  *
  * 這異常沒有被 ResponseStatus 標註,必須被 handler 捕捉
  *
  * 本類中的 handleError 方法捕獲了它,同時,該方法指定了 model 和 view,對比上面那兩對
  *
  * @return 什麼也不返回,總是丟擲異常
  */
 @RequestMapping("/supportInfoException")
 public String throwCustomException() {
     throw new SupportInfoException("出錯了");
 }

 /**
  * 完全控制 - model 、view 、有用的異常資訊
  *
  * @param req 當前的 HTTP 請求物件.
  * @param exception 丟擲的異常 - 也就是 {@link SupportInfoException}.
  * @return 模型和檢視
  * @throws Exception 異常
  */
 @ExceptionHandler(SupportInfoException.class)
 public ModelAndView handleError(HttpServletRequest req, Exception exception) throws Exception {
     if (AnnotationUtils.findAnnotation(exception.getClass(), ResponseStatus.class) != null)
         throw exception;

     logger.error("Request: " + req.getRequestURI() + " raised " + exception);

     ModelAndView mav = new ModelAndView();
     // 注意這裡,這個 exception 是一個物件 | 而 DefaultErrorAttributes 中的 exception 是異常物件的完全限定名
     mav.addObject("exception", exception);
     mav.addObject("url", req.getRequestURL());
     mav.addObject("timestamp", new Date().toString());
     mav.addObject("status", 500);

     mav.setViewName("support.error");
     return mav;
 }

 // @formatter:off
 //
 // 注意:上面的三種異常處理方式的共同特點都是在 controller 內部使用 ExceptionHandler 來處理,我們可以將之稱為"本地異常處理"
 //
 // @formatter:on

解釋一下程式碼:我們在控制器的三個方法中丟擲異常三個,然後使用對應的異常處理方法進行處理,這裡的關鍵是@ExceptionHandler這個註解,正是它將異常捕獲,然後在異常處理方法中進行處理。

  • 從上面的程式碼中可以看出,第一種方式沒有指定錯誤頁面(使用預設的),而第二種和第三種方式都指定了錯誤頁面,下面是三個異常頁面
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title>Title</title>
    <link th:href="@{webjars/bootstrap/css/bootstrap.min.css}" rel="stylesheet"/>
</head>
<body>

<h2>Spring Boot </h2>

<h3>
    需要注意的是:在實際的開發中,絕對不應該向使用者展示 Java 異常
</h3>

<!-- 下面這些關鍵字是在 org.springframework.boot.autoconfigure.web.DefaultErrorAttributes 中定義的 -->

<p th:if="${exception}">
    <b>異常物件的型別:</b>
    <span th:text="${exception}">exception type ...</span>
</p>

<p th:if="${timestamp}">
    <b>發生時間:</b>
    <span th:text="${timestamp}">timestamp ...</span>
</p>

<p th:if="${status}">
    <b>響應狀態碼:</b>
    <span th:text="${status}">status code ...</span>
    <span th:if="${error}" th:text="'('+${error}+')'">error ...</span>
</p>

<p th:if="${message} and ${message.length() != 0}">
    <b>錯誤原因:</b>
    <span th:if="${message} and ${message.length() != 0}" th:text="${message}">message ...</span>
</p>

<p th:if="${path}">
    <b>請求路徑:</b>
    <span th:text="${path}">path ...</span>
</p>

</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title>Title</title>
    <link th:href="@{webjars/bootstrap/css/bootstrap.min.css}" rel="stylesheet"/>
</head>
<body>

<h2>DataBase 錯誤頁面</h2>

<h3>
    需要注意的是:在實際的開發中,絕對不應該向使用者展示 Java 異常
</h3>

<h3>
    該方式幾乎不能為前端提供有效的異常資訊
</h3>

<!-- 下面這些關鍵字是在 org.springframework.boot.autoconfigure.web.DefaultErrorAttributes 中定義的 -->

<p th:if="${exception}">
    <b>異常物件的型別:</b>
    <span th:text="${exception}">exception type ...</span>
</p>

<p th:if="${timestamp}">
    <b>發生時間:</b>
    <span th:text="${timestamp}">timestamp ...</span>
</p>

<p th:if="${status}">
    <b>響應狀態碼:</b>
    <span th:text="${status}">status code ...</span>
    <span th:if="${error}" th:text="'('+${error}+')'">error ...</span>
</p>

<p th:if="${message} and ${message.length() != 0}">
    <b>錯誤原因:</b>
    <span th:if="${message} and ${message.length() != 0}" th:text="${message}">message ...</span>
</p>

<p th:if="${path}">
    <b>請求路徑:</b>
    <span th:text="${path}">path ...</span>
</p>

</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title>Title</title>
    <link th:href="@{webjars/bootstrap/css/bootstrap.min.css}" rel="stylesheet"/>
</head>
<body>
<h1>Support Friendly Error Page</h1>

<!--  As we are using Thymeleaf, you might consider using
      ${#httpServletRequest.requestURL}. But that returns the path
      to this error page.  Hence we explicitly add the url to the
      Model in some of the example code. -->
<p th:if="${url}">
    <b>請求地址:</b> <span th:text="${url}">Page URL</span>
</p>

<!-- 自定義的,這裡的 exception 是一個物件 -->
<p th:if="${exception}">
    <b>錯誤訊息:</b>
    <span th:text="${exception.message}">exception message ...</span>
</p>

<p th:if="${timestamp}">
    <b>發生時間:</b>
    <span th:text="${timestamp}">Timestamp</span>
</p>

<p th:if="${status}">
    <b>響應狀態碼:</b>
    <span th:text="${status}">status-code</span>
    <span th:if="${error}" th:text="'('+${error}+')'">error ...</span>
</p>

</body>
</html>
  • 最後,通過瀏覽器檢視一下
    這裡寫圖片描述
    這裡寫圖片描述
    這裡寫圖片描述

  • 參考