在開發 Spring Boot 時,當我們丟出一個 exception,Spring Boot 的 exception handler 會幫我們處理它,並且傳回一個 JSON 給前端。這個 JSON 的內容大致如下方所示。我們可以看出這個 JSON 的 message 欄位是對應到 exception.message。也就是說,欄位 status
和 error
都是我們無法控制的。而且,你也無法在這 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" }