Spring Boot 自定義 Exception Handling

Photo by rehan shaik on Unsplash
Photo by rehan shaik on Unsplash
在開發 Spring Boot 時,當我們丟出一個 exception,Spring Boot 的 exception handler 會幫我們處理它,並且傳回一個 JSON 給前端。我們將在本文章中,說明如何自訂義這個回傳給前端的 JSON 內容。

在開發 Spring Boot 時,當我們丟出一個 exception,Spring Boot 的 exception handler 會幫我們處理它,並且傳回一個 JSON 給前端。這個 JSON 的內容大致如下方所示。我們可以看出這個 JSON 的 message 欄位是對應到 exception.message。也就是說,欄位 statuserror 都是我們無法控制的。而且,你也無法在這 JSON 中增加其他的欄位。我們將在本文章中,說明如何自訂義這個回傳給前端的 JSON 內容。

{
    "timestamp": "2021-05-05T03:16:49.588+00:00",
    "status": 403,
    "error": "Forbidden",
    "message": "Access is denied",
    "path": "/path/to/api"
}

假設我們在程式中自訂義以下的 exception 叫 AppException。而且,我們還新增一個參數 code

class AppException(
    val code: Int,
    message: String? = null,
    cause: Throwable? = null
) : Exception(message, cause)

接下來,我們新增一個 class ExceptionHandler,並且在 ExceptionHandler 上加上 @ControllerAdvice annotation。@ControllerAdvice 是一種 @Component,特別是用來宣告 @ExceptionHandler@InitBinder、和 @ModelAttribute 方法。所以,加上 @ControllerAdivce 後,ExceptionHandler 也就會是一個 bean。

再來,我們宣告 data class ExceptionResponse 來包含要回傳給前端的資料。ExceptionResponse 的結構與 Spring Boot 預設回傳給前端的 JSON 相似,但多了一個參數 code

最後,我們宣告一個方法 handleAppRequest,並且加上 @ExceptionHandler annotation。在 @ExceptionHandler 中,指定要處理的 exception 的 class,即 AppException。

@ControllerAdvice
class ExceptionHandler {
    data class ExceptionResponse(
        val code: Int,
        val status: Int,
        val error: String,
        val message: String,
        val path: String,
        val timestamp: Date = Date(),
    )

    @ExceptionHandler(AppException::class)
    fun handleAppRequest(
        exception: AppException,
        request: HttpServletRequest,
    ): ResponseEntity<ExceptionResponse> {
        return ResponseEntity(
            ExceptionResponse(
                exception.code,
                HttpStatus.BAD_REQUEST.value(),
                "Bad Request",
                exception.message ?: "",
                request.requestURI,
            ),
            HttpStatus.BAD_REQUEST,
        )
    }
}

我們看到 handleAppRequest() 回傳 ResponseEntity<ExceptionResponse>。我們也可以讓 handleAppRequest() 直接回傳 ExceptionResponse。其程式碼如下:

@ExceptionHandler(AppBadRequestException::class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
fun handleBadRequest(
    exception: AppBadRequestException,
    request: HttpServletRequest,
): ExceptionResponse {
    return ExceptionResponse(
        exception.code,
        HttpStatus.BAD_REQUEST.value(),
        "Illegal access",
        exception.message ?: "",
        request.requestURI,
    )
}

最後,用以下的程式碼丟出一個 exception 來看看回傳的結果。

throw AppBadRequestException(4, "Use is not allowed to access the resource")

前端會收到以下的 JSON 資料。

{
    "code": 4,
    "status": 400,
    "error": "Illegal access",
    "message": "Use is not allowed to access the resource",
    "path": "/path/to/api",
    "timestamp": "2021-05-05T04:30:12.754+00:00"
}
發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

You May Also Like
Photo by Charles Jackson on Unsplash
Read More

Springdoc-OpenAPI 教學

Springdoc 是一個整合 OpenAPI Specification 和 Spring Boot 的套件。和 SpringFox 套件一樣,它產出 Swagger 文件。兩者不同在於,Springdoc 是用 Swagger 3,而 SpringFox 是用 Swagger 2。
Read More