Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot Rest responding with empty body for exceptions other than the ones overridden in my @ControllerAdvice

I have a @ControllerAdvice extending ResponseEntityExceptionHandler as an attempt for me to control standard response for any exception raised with in the API call workflow.

Without the Controller advice. I get HTML based generic response generated by spring with correct response headers. But when I add my @ControllerAdvice, Spring doesn't response with generic error body. The body is empty with correct response headers

@Override
protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
        HttpHeaders headers, HttpStatus status, WebRequest request) {

        String erroMessage = "Required Parameter: '"+ex.getParameterName()+"' was not available in the request.";
        TrsApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, erroMessage, ex, ApiErrorCode.INVALID_REQUEST);
        return buildResponseEntity(apiError);
}

So, now in case of a required parameter missing in the request, the flow beautifully trigger my overridden implementation and responds with JSON payload describing the error. But, in case of any other exception like HttpMediaTypeNotAcceptableException, spring is responding with empty body.

Before I added my advice, spring was responding with generic error response. I am new to spring boot ecosystem. Need help in understanding if this is an expected behavior of if there is a better approach of achieving centralized error handling.

like image 212
Gagandeep Singh Avatar asked Jun 10 '19 14:06

Gagandeep Singh


1 Answers

I guess that I found out a solution for swallowed body when ControllerAdvice class is extending ResponeEntityExceptionHandler. In my case the setup looks like that:

@ControllerAdvice
@Slf4j
class GlobalExceptionHandlers extends ResponseEntityExceptionHandler {

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
                                      MethodArgumentNotValidException exception,
                                      HttpHeaders headers,
                                      HttpStatus status,
                                      WebRequest request) {
        // logic that creates apiError object (object with status, message, errorCode, etc)
        //...
        return handleExceptionInternal(exception, apiError, headers, status, request);
    }

And this worked like a charm for exceptions of class MethodArgumentNotValidException. But it broke all other exceptions handled by ResponseEntityExceptionHandler, and returned empty response body for them.

But the fix is easy, just override handleExceptionInternal from ResponseEntityExceptionHandler:

@ControllerAdvice
@Slf4j
class GlobalExceptionHandlers extends ResponseEntityExceptionHandler {

    /// ... code from previous snippet

    @Override
    protected ResponseEntity<Object> handleExceptionInternal(
                                      Exception exception, 
                                      Object body, 
                                      HttpHeaders headers, 
                                      HttpStatus status, 
                                      WebRequest request) {
        // for all exceptions that are not overriden, the body is null, so we can
        // just provide new body based on error message and call super method
        var apiError = Objects.isNull(body) 
                ? new ApiError(status, exception.getMessage()) // <-- 
                : body;
        return super.handleExceptionInternal(exception, apiError, headers, status, request);
    }
}
like image 102
Pawel Kiszka Avatar answered Sep 28 '22 18:09

Pawel Kiszka