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?
We can use @ResponseStatus to mark a method or an exception class with a status code and reason that should be returned. On invoking the marked handler method or when a specified exception is thrown, the HTTP status will be set to the one defined using @ResponseStatus annotation.
You can add extra ( @ExceptionHandler ) methods to any controller to specifically handle exceptions thrown by request handling ( @RequestMapping ) methods in the same controller. Such methods can: Handle exceptions without the @ResponseStatus annotation (typically predefined exceptions that you didn't write)
Spring MVC provides exception handling for your web application to make sure you are sending your own exception page instead of the server-generated exception to the user. The @ExceptionHandler annotation is used to detect certain runtime exceptions and send responses according to the exception.
@ControllerAdvice is not specific to the exception handling , its also used for handling property, validation or formatter bindings at the global level. @ControllerAdvice in the context of exception handling is just another way of doing exception handling at a global level using @Exceptionhandler annotation.
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
}
}
}
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() {}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With