mvlupan
mvlupan

Reputation: 3646

@ControllerAdvice overrides exception @ResponseStatus

I'm not sure why but the @ControllerAdvice is overriding the response code defined at Exception level using the @ResponseStatus annotation.

Exception:

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class GreetException extends RuntimeException {}

Controller:

@RestController
@RequestMapping("/")
public class GreetController {

    @RequestMapping(method = RequestMethod.GET)
    public String greet() {
        throw new GreetException();
    }
}

Controller Advice:

@ControllerAdvice
public class ExceptionConfiguration {

    @ResponseStatus(HttpStatus.CONFLICT)
    @ExceptionHandler(RuntimeException.class)
    public void handleConflict() {}
}

When the greet method from the GreetController gets called the response is 409 - CONFLICT. Because i specifically provided a response code on exception level I expected that will be the returned code (400 - BAD_REQUEST).

Of-course this is an overly simplified example but we defined an controller advice with RuntimeException definition so we could assign an id to every exception that was not caught.

What is the correct way to achieve the expected behaviour?

Upvotes: 11

Views: 10913

Answers (2)

Vasu
Vasu

Reputation: 22442

Your ControllerAdvice does not know about GreetException, so it is throwing "HttpStatus.CONFLICT" instead of "HttpStatus.BAD_REQUEST"

Please find the below spring documentation for your ref:

https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

Please change the ControllerAdvice code as below:

@ControllerAdvice
public class ExceptionConfiguration {

    @ResponseStatus(HttpStatus.CONFLICT)
    @ExceptionHandler(RuntimeException.class)
    public void handleRuntimeException() {}

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(GreetException.class)
    public void handleGreetException() {}

}

Upvotes: 3

Fabian Nack
Fabian Nack

Reputation: 724

The problem with annotating the exception with @ResponseStatus is, that this only triggers when the exception is not handled somewhere else.

See this quote from a Spring article about exception handling in Spring MVC:

When an annotated exception is thrown from a controller method, and not handled elsewhere, it will automatically cause the appropriate HTTP response to be returned with the specified status-code.

The same article describes the approach to make it work despite having a ControllerAdvice with a general handler. See Paragraph about ControllerAdvice.

What you basically would have to do is to rethrow the exception inside your general handler if it is annotated with @ResponseStatus:

@ControllerAdvice
public class ExceptionConfiguration {

  @ResponseStatus(HttpStatus.CONFLICT)
  @ExceptionHandler(RuntimeException.class)
  public void handleConflict(RuntimeException e) throws RuntimeException {
    // Check for annotation
    if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
      // Rethrow Exception
      throw e;
    }
    else {
      // Provide your general exception handling here
    }
  }
}

Upvotes: 14

Related Questions