iT邦幫忙

2023 iThome 鐵人賽

DAY 18
0
Mobile Development

Spring Boot+Android 30天 實戰開發 系列 第 18

【Day - 18】Spring Boot統一異常處理: 優化您的應用程式錯誤處理 (下)

  • 分享至 

  • xImage
  •  

5. 異常的分類和處理

在實際應用中,異常不是一個單一的概念,而是可以分為多種類型,每種類型的異常可能需要不同的處理邏輯。為了更好地管理和處理異常,我們可以將它們分為不同的類別,並為每個類別定義特定的處理方法。

5.1 分類不同類型的異常

通常,我們可以將異常分為兩大類別:業務異常和系統異常。

  • 業務異常:這些異常是由應用程式的業務邏輯引起的,通常是應用程式的預期行為之一。例如,當使用者輸入無效的數據或請求某個資源時不存在,我們可以將其歸類為業務異常。
  • 系統異常:這些異常是由系統或環境問題引起的,通常不是預期的行為。例如,數據庫連接失敗、磁盤空間不足或其他基礎設施問題都可能引發系統異常。

5.2 不同類型的異常處理

針對不同類型的異常,我們可以定義不同的處理邏輯,以確保我們能夠適當地應對各種情況。

  • 業務異常處理
    對於業務異常,我們通常希望將有關錯誤的具體信息返回給客戶端,以便客戶端可以理解問題並采取適當的措施。這包括返回適當的HTTP狀態程式碼(如400 Bad Request),並將錯誤信息包含在響應中,使客戶端能夠識別並處理異常。

以下是處理業務異常的示例程式碼:

@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorDto handleBusinessException(BusinessException ex, HttpServletRequest request) {
    ErrorDto errorDto = new ErrorDto();
    errorDto.setTimestamp(LocalDateTime.now());
    errorDto.setStatus(HttpStatus.BAD_REQUEST.value());
    errorDto.setError("Bad Request");
    errorDto.setMessage(ex.getMessage());
    errorDto.setPath(request.getRequestURI());
    return errorDto;
}

在這個示例中,我們捕獲了一個 BusinessException,並返回一個帶有400 Bad Request狀態的錯誤DTO,其中包含有關業務異常的詳細信息。

  • 系統異常處理
    對於系統異常,我們通常不希望將詳細的錯誤信息返回給客戶端,因為這可能包含敏感信息或有安全風險。相反,我們可以簡單地返回一個適當的HTTP狀態程式碼(如500 Internal Server Error)以指示問題,但不包含具體的錯誤信息。

以下是處理系統異常的示例程式碼:

@ExceptionHandler(SystemException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public void handleSystemException(SystemException ex) {
    // 無需返回具體的錯誤信息
}

在這個示例中,我們捕獲了一個 SystemException,並返回500 Internal Server Error,但不包含具體的錯誤信息。

通過將異常分為不同類型並定義相應的處理邏輯,我們可以更好地管理和處理異常,提高我們應用程式的可靠性和可維護性。這也有助於確保客戶端獲得有意義的錯誤信息,以更好地理解和處理問題。

5.3 簡單示例:異常的分類和處理

在這個示例中,我們將創建一個簡單的 Spring Boot 應用程式,然後添加自定義異常處理,分為業務異常和系統異常兩類。

(1) 首先,讓我們創建一個 Spring Boot 應用程式:
(2) 創建一個名為 "ExceptionHandlingDemo" 的 Spring Boot 項目。
(3) 在pom.xml文件中,確保你有以下相依性:

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

(4) 接下來,我們將創建自定義的業務異常和系統異常以及相應的異常處理器。
(4.1) 首先,創建一個 BusinessException 類,代表業務異常:

public class BusinessException extends RuntimeException {
    public BusinessException(String message) {
        super(message);
    }
}

(4.2) 接著,創建一個 SystemException 類,代表系統異常:

public class SystemException extends RuntimeException {
    public SystemException(String message) {
        super(message);
    }
}

(5) 現在,我們創建一個統一異常處理器,這個處理器將處理這兩種不同類型的異常。創建一個名為 GlobalExceptionHandler 的類:

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorResponse handleBusinessException(BusinessException ex) {
        return new ErrorResponse(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
    }

    @ExceptionHandler(SystemException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public void handleSystemException(SystemException ex) {
        // 不返回具體的錯誤信息
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public ErrorResponse handleOtherExceptions(Exception ex) {
        return new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Internal Server Error");
    }
}

在這個處理器中,我們使用 @ExceptionHandler 注解處理不同類型的異常。對於業務異常,我們返回一個包含錯誤信息的 ErrorResponse 物件,並將 HTTP 狀態設置為 400 Bad Request。對於系統異常,我們不返回具體的錯誤信息,只是設置 HTTP 狀態為 500 Internal Server Error。對於其他未處理的異常,我們也返回一個 ErrorResponse 物件,並設置 HTTP 狀態為 500。

(6) 最後,我們創建一個 ErrorResponse 類,用於包裝錯誤信息:

@Data // Lombok註解
@Builder // Lombok註解
public class ErrorResponse {
    private int status;
    private String message;
}

(7) 現在,我們可以在我們的應用程式中引發業務異常和系統異常,並確保它們由我們的自定義異常處理器處理。例如:

@RestController
@RequestMapping("/api")
public class BookController {

    @GetMapping("/books/{id}")
    public ResponseEntity<Book> getBook(@PathVariable Long id) {
        // 模擬業務異常
        if (id == 1) {
            throw new BusinessException("Book not found with id " + id);
        }

        // 模擬系統異常
        if (id == 2) {
            throw new SystemException("Internal Server Error");
        }

        // 正常情況下返回書籍信息
        Book book = new Book(id, "Sample Book");
        return ResponseEntity.ok(book);
    }
}

在這個示例中,我們創建了一個 BookController 並在 getBook 方法中引發了業務異常和系統異常。根據異常的類型,我們的自定義異常處理器會返回適當的 HTTP 狀態和錯誤信息。

6. 異常日誌記錄

在 Spring Boot 中,除了統一處理和返回異常信息外,日誌記錄對於診斷和追蹤應用程式中的異常情況也至關重要。 Spring Boot 集成了許多常見的日誌記錄框架,如 Logback、Log4j2 和 Java Util Logging,讓您可以輕松記錄異常信息。

以下是使用 Log4j2 作為日誌框架時,以下是如何配置和使用 Log4j2 來記錄異常信息的實踐步驟:

  1. 選擇 Log4j2 日誌框架: 確保您的 Spring Boot 項目中包含 Log4j2 的相關依賴,或者您可以在 pom.xml 文件中添加以下依賴:

    <dependency>
      <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
    </dependency>
    
  2. 配置 Log4j2: 在您的項目中,創建一個 Log4j2 配置文件,通常命名為 log4j2.xml。您可以將該文件放置在項目的 src/main/resources 目錄下。以下是一個簡單的 log4j2.xml 配置示例:

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="INFO">
      <Appenders>
          <Console name="Console" target="SYSTEM_OUT">
              <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
          </Console>
          <File name="File" fileName="logs/exception.log">
              <PatternLayout>
                  <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Pattern>
              </PatternLayout>
          </File>
      </Appenders>
      <Loggers>
          <Root level="error">
              <AppenderRef ref="Console"/>
              <AppenderRef ref="File"/>
          </Root>
      </Loggers>
    </Configuration>
    

    上面的配置示例將錯誤級別(error)的日誌信息同時輸出到控制台和 logs/exception.log 文件中。

  3. 在異常處理中添加日誌記錄: 在您的自定義全局異常處理器方法中,使用 Log4j2 來記錄異常信息。例如:

    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        // ...
    
        @ExceptionHandler(BusinessException.class)
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        @ResponseBody
        public ErrorResponse handleBusinessException(BusinessException ex) {
            // 記錄業務異常
            Logger logger = LogManager.getLogger(GlobalExceptionHandler.class);
            logger.error("BusinessException: " + ex.getMessage(), ex);
            return new ErrorResponse(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
        }
    
        // ...
    }
    

    這裡,我們創建了一個 Logger 實例並使用 error 級別記錄異常信息。

  4. 集成監控和警報工具(可選): 根據您的項目需求,您還可以將 Log4j2 集成到監控和警報工具中,以實現更高級的異常監控和警報。

以上是使用 Log4j2 記錄異常信息的基本實踐步驟。通過選擇適當的日誌框架、配置日誌屬性、在異常處理中添加日誌記錄,您可以更好地追蹤和診斷應用程式中的異常情況,提高應用程式的可靠性和可維護性。

7. 總結

在這篇文章中,我們深入探討了 Spring Boot 中的統一異常處理的實踐。現在,讓我們回顧一下我們所學的主要知識點,並強調這些實踐的價值和優勢。

  • 關鍵知識點回顧
    • 異常處理的重要性: 我們開始了解異常處理在 Web 應用中的重要性。適當的異常處理可以提高使用者體驗,幫助追蹤和診斷問題,以及提高應用程式的可維護性。
    • Spring Boot 異常處理基礎: 我們介紹了 Spring Boot 中異常處理的基本概念,並解釋了異常處理的工作原理。我們強調了統一異常處理的好處,而不是散落的異常處理程式碼。
    • 統一異常處理器的創建: 我們指導了如何創建自定義的異常處理器,使用 @ControllerAdvice@ExceptionHandler 注解處理不同類型的異常情況。我們還演示了如何編寫通用的異常處理邏輯。
    • 統一返回結構: 我們規範化了異常響應的數據結構,並示範了如何使用 DTO(數據傳輸物件)來包裝異常信息,以實現一致性的響應。
    • 異常的分類和處理: 我們學習了如何區分不同類型的異常,例如業務異常和系統異常,並示範了如何針對不同類型的異常實現不同的處理邏輯。
    • 異常日誌記錄: 我們為異常處理添加了日誌記錄,使用 Spring Boot 的日誌框架(例如 Log4j2)記錄異常信息。我們還提供了集成日誌記錄框架的最佳實踐和建議。

統一異常處理和自定義錯誤頁面的實踐是開發 Web 應用程式不可或缺的一部分。它有助於提供更好的使用者體驗,確保應用程式能夠恢復正常運行,並簡化了錯誤處理程式碼的管理。

我們鼓勵您在自己的應用程式中實施良好的異常處理實踐,以提高使用者滿意度、提供更好的使用者反饋和快速解決問題。請記住,異常處理是不斷改進和調試的過程,因此要隨時準備應對新的挑戰和需求。


上一篇
【Day - 17】Spring Boot統一異常處理: 優化您的應用程式錯誤處理 (上)
下一篇
【Day - 19】Spring Boot 集成 Thymeleaf構建動態Web應用 (上)
系列文
Spring Boot+Android 30天 實戰開發 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言