@ControllerAdvice and @ExceptionHandler not getting triggered for my RestController
Asked Answered
E

5

10

In order to have unified exception handling throughout the application I am using Error Handling for REST with Spring solution#3 which uses @ControllerAdvice along with @ExceptionHandler.

Spring version: 4.3.22.RELEASE

Spring Boot version: 1.5.19.RELEASE

This is a Spring boot application and following is my package structure.

src/main/java
  com.test.app.controller
     MyRestController.java       -- This is my Rest controller
  com.test.app.handler
     RestExceptionHandler.java   -- This is my ControllerAdvice class

Following is my ControllerAdvice code and one of the Controller throws InvalidDataException but still the corresponding @ExceptionHandler is not called. Instead I am getting Unexpected 'e' as the response body with http 400.

@ControllerAdvice
public class RestExceptionHandler {

    @ExceptionHandler(InvalidDataException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public @ResponseBody ErrorResponse handleValidationError(final InvalidDataException ex,
                                                             final WebRequest request) {
        log.error("InvalidDataException message:{} ", ex.getMessage());
        return getExceptionResponse("Failed with Invalid data" + ex.getMessage(), HttpStatus.BAD_REQUEST.value());
    }


    private ErrorResponse getExceptionResponse(final String message, final Integer errorCode) {
        final ErrorResponse exceptionResponse = new ErrorResponse();
        exceptionResponse.setErrorCode(errorCode.toString());
        exceptionResponse.setErrorDescription(message);
        log.error("message:{}", exceptionResponse);
        return exceptionResponse;
    }
}

I looked at other post on SO as well as other forums where they mentioned to use @EnableWebMvc and @ComponentScan etc. but nothing helped. Could someone please help me understand what am I missing?

Following is my Controller and corresponding interface.

@RestController
public class MyRestController implements MyApi {

    @Override
    public ResponseEntity<List<MyResponse>> myGet(@RequestHeader(value = "a") String a,
                                                               @RequestHeader(value = "b") String b,
                                                               @RequestHeader(value = "c") String c,
                                                               @RequestHeader(value = "d") String d,
                                                               @RequestHeader(value = "e") String e,
                                                               @RequestHeader(value = "f") String f,
                                                               @RequestHeader(value = "g") String g) {

      List<MyResponse> responses = service.getData(c, d, e, f); // This throws exception
      return new ResponseEntity<>(responses, HttpStatus.OK);
    }
}


@Validated
@Api(value = "My", description = "the My API")
//This is generated interface through swagger codegen
public interface MyApi {

    @ApiOperation(value = "", nickname = "myGet", notes = "", response = MyResponse.class, responseContainer = "List")
    @ApiResponses(value = {
        @ApiResponse(code = 200, message = "normal response", response = MyResponse.class, responseContainer = "List"),
        @ApiResponse(code = 400, message = "Request is invalid", response = ErrorResponse.class),
        @ApiResponse(code = 401, message = "", response = ErrorResponse.class),
        @ApiResponse(code = 404, message = "", response = ErrorResponse.class),
        @ApiResponse(code = 405, message = "", response = ErrorResponse.class),
        @ApiResponse(code = 409, message = "", response = ErrorResponse.class),
        @ApiResponse(code = 500, message = "Internal Server Error", response = ErrorResponse.class),
        @ApiResponse(code = 503, message = "Service Unavailable", response = ErrorResponse.class) })
    @RequestMapping(value = "/v1/test",
        produces = { "application/json" }, 
        method = RequestMethod.GET)
    default ResponseEntity<List<MyResponse>> myGet(@ApiParam(value = "a" ,required=true) @RequestHeader(value="a", required=true) String a,
                                                   @ApiParam(value = "b" ,required=true) @RequestHeader(value="b", required=true) String b,
                                                   @ApiParam(value = "c" ,required=true) @RequestHeader(value="c", required=true) String c,
                                                   @ApiParam(value = "d" ,required=true) @RequestHeader(value="d", required=true) String d,
                                                   @ApiParam(value = "e" ,required=true) @RequestHeader(value="e", required=true) String e,
                                                   @ApiParam(value = "f" ,required=true) @RequestHeader(value="f", required=true) String f,
                                                   @ApiParam(value = "g" ,required=true) @RequestHeader(value="g", required=true) String g) {
        getRequest().ifPresent(request -> {
            for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
                if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
                    ApiUtil.setExampleResponse(request, "application/json", "{  \"aNum\" : 0,  \"cNum\" : \"cNum\"}");
                    break;
                }
            }
        });
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
    }
}

Following is the code snippet from my GlobalExceptionHandler

class GlobalExceptionHandler extends ExceptionHandlerExceptionResolver implements HandlerExceptionResolver, Ordered, InitializingBean {
    ...
    @Override
    protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
        if (exception instanceof com.myframework.SystemException) {
            return new ServletInvocableHandlerMethod(this, exceptionMethods.get(com.myframework.SystemException.class.getName()));
        } else if (exception instanceof GenericApplicationException) {
            return new ServletInvocableHandlerMethod(this, exceptionMethods.get(com.myframework.GenericApplicationException.class.getName()));
        } else {
            return null;
        }
    }
    ....
}
Excretion answered 3/9, 2019 at 22:10 Comment(9)
Show us the code of your MyRestController.Tribunate
@Tribunate I have added the Controller code in the question, pls take a look.Excretion
'Instead I am getting Unexpected 'e' as the' - do this appear in the console in a stack trace? If so, please, paste that stack trace in the question as wellFlugelhorn
I get Unexpected 'e' in the response body with HTTP code 400. There is no exception trace in the ConsoleExcretion
So what would you expect instead? Seems like your error handler is working fine. "Unexpected 'e' " is the exception message coming from your service, it is returned with http400 instead of http500 for unhandled exceptions.Voronezh
If you don't see an error log, maybe your logging configuration is wrong.Voronezh
also you should show the URL you use when you get the error, and possibly the parts of your main application config where you include both the controller and the ControllerAdviceVoronezh
what exception is code throwing and what does the stacktrace look like?Kremer
@KalpeshSoni I get Unexpected 'e' in the response body with HTTP code 400. There is no exception trace in the Console.Excretion
A
9

It should work. Some possible causes that make it fails may be :

  1. The RestExceptionHandler is not declared as a spring bean yet ? @SpringBootApplication by default will only scan the spring beans to register under its package and all of its sub-packages.

  2. The controller is actually not throwing InvalidDataException but other exception ?

Anyway I suggest you to make the following changes to check if RestExceptionHandler is get called .

In your spring boot main application class , explicitly register RestExceptionHandler as a spring bean using @Import

@SpringBootApplication
@Import(RestExceptionHandler.class)
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Also in the RestExceptionHandler , also include a method to catch the most generic Exception :

@ControllerAdvice
public class RestExceptionHandler {

    @ExceptionHandler(Exception.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public ErrorResponse handleGenericException(final Exception ex ,final WebRequest request) {
        System.out.println("handleGenericException ....");
        return getExceptionResponse("Failed with Invalid data" + ex.getMessage(), HttpStatus.BAD_REQUEST.value());
    }
}

Please let me know if the RestExceptionHandler will get called after making these changes.

Aviatrix answered 9/9, 2019 at 19:9 Comment(9)
Sure will try and let you know.Excretion
Hi @Ken, Thanks for your answer. As we are using an in-house framework I believe that is interfering with the error handling, and I see that following class from the framework gets called class GlobalExceptionHandler extends ExceptionHandlerExceptionResolver implements HandlerExceptionResolver, Ordered, InitializingBean {} and following method from that class gets called: @Override protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {..}Excretion
Yes , I guess most probably it is interfering , for example your customised GlobalExceptionHandler may be configured to have higher priority to resolve he exception than the built-in one and it somehow cannot handle @ControllerAdvice....Aviatrix
I did give @Order(Ordered.HIGHEST_PRECEDENCE) on my RestExceptionHandler but even then it does not get called. How to have it call my handler?Excretion
Your customised GlobalExceptionHandler are overriding getExceptionHandlerMethod() which contain the logic of how to call the @ControllerAdvice bean. I am guessing are you override the logic about getting @ControllerAdvice ? As you do not show more codes about how to configure the exception handlers and what the GlobalExceptionHandler looks like , I can only guess ...Aviatrix
I added the code snippet from my GlobalExceptionHandler in the question above, please let me know if more details are needed.Excretion
Did you get a chance to look at my above comment?Excretion
Not sure why you override getExceptionHandlerMethod() in GlobalExceptionHandler as you are override the logic to call @ControllerAdvice bean. Can you remove them to see if your @ControllerAdvice get called ?Aviatrix
Or change the return null to return super.getExceptionHandlerMethod( handlerMethod, exception) Aviatrix
K
1

Make sure you have no other spring components that extend

AbstractErrorController
Kremer answered 9/9, 2019 at 18:32 Comment(1)
I don't have that anywhere.Excretion
R
1

below code was enough for me to make the Adviser class workable in the Controller class

@SpringBootApplication
@Import(RestExceptionHandler.class)
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
Remunerate answered 25/4, 2023 at 5:8 Comment(0)
P
0

Below controller advice should solve the problem.

@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

 @ExceptionHandler(value = {InvalidDataException.class})
 protected ResponseEntity<Object> handleInvalidDataException(
  RuntimeException ex, WebRequest request) {
    return new ResponseEntity<>(getExceptionResponse("Failed with Invalid data" + ex.getMessage(), HttpStatus.BAD_REQUEST.value()), HttpStatus.BAD_REQUEST);
}
Phyte answered 9/9, 2019 at 18:47 Comment(3)
Here's a demo project of Global Error Handler in Spring Boot github.com/s2agrahari/global-excpetion-handler-spring-bootPhyte
Don't think it needs to extend ResponseEntityExceptionHandler . I have use many @ControllerAdvice without extending ResponseEntityExceptionHandler in many projects and work very well..Aviatrix
Thanks @KenChan for reporting. Updated the answerPhyte
P
0

replace @ControllerAdvice with @RestControllerAdvice

Peerage answered 18/8, 2021 at 13:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.