Customize Exception Handling in Spring Boot

Photo by rehan shaik on Unsplash
Photo by rehan shaik on Unsplash
When developing Spring Boot, when we throw an exception, Spring Boot’s exception handler will help us deal with it and send back a JSON to the front end. In this article, we will explain how to customize the JSON content returned to the front end.

When developing Spring Boot, when we throw an exception, Spring Boot’s exception handler will help us deal with it and send back a JSON to the front end. The content of this JSON is roughly as shown below. We can see that the property message of this JSON corresponds to exception.message. In other words, the property status and error are beyond our control. Moreover, you cannot add other properties to this JSON. In this article, we will explain how to customize the JSON content returned to the front end.

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

Suppose we customize the following exception called AppException, where we have added a new parameter code.

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

Next, we create class ExceptionHandler, and add @ControllerAdvice annotation to the ExceptionHandler . @ControllerAdvice is a kind of @Component , especially used to declare @ExceptionHandler , @InitBinder , and @ModelAttribute methods. So, after adding @ControllerAdivce, ExceptionHandler will also be a bean.

We declare data class ExceptionResponse to contain the data you want to send back to the front end. The structure of ExceptionResponse is similar to the JSON that Spring Boot send back to the front end by default, but with one more parameter code.

Finally, we declare a method handleAppRequest to handle our exception, and add @ExceptionHandler annotation. In @ExceptionHandler, specify the class of the exception to be handled, that is, 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,
        )
    }
}

We see that handleAppRequest() returns ResponseEntity<ExceptionResponse>. We can also let handleAppRequest() directly return ExceptionResponse. The code is as follows:

@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,
    )
}

Finally, use the following code to throw an exception to see the returned result.

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

The front end will receive the following JSON data.

{
    "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"
}
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like