I'm writing a Spring Boot Application. And I'm implementing the exception handling right now.
I got the following problem.
My exception handlers look like this:
#ControllerAdvice
public class SpecialExceptionHandler {
#ExceptionHandler(MissingServletRequestParameterException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseObject missingServletErrorHandler(HttpServletRequest req, MissingServletRequestParameterException exception) {
//do something
return responseObject;
}}
And I got a general exception handler which looks
#ControllerAdvice
public class GeneralExceptionHandler {
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseObject defaultErrorHandler(HttpServletRequest req, Exception exception) {
// do something
return responseObject;
}}
But I got a problem: my application always runs into the GeneralExceptionHandler instead of the special handler unless I change the name of the GeneralExceptionHandler class to a name which comes alphabetically after the special exception handler (e.g. change 'GeneralExceptionHandler' to 'zGeneralExceptionHandler').
How can I resolve this issue?
You could try adding #Order(N) annotations on your ControllerAdvices, to force their registration order (where N is a int defining the order)
Related
I have my Spring error controller, and I need to get the actual exception class so I can print stack trace and other things like that.
This is my error controller
#Controller
public class ErrorController implements org.springframework.boot.web.servlet.error.ErrorController {
#RequestMapping("/error")
public String handleError() {
return "somethingwentwrong";
}
#Override
public String getErrorPath() {
return null;
}
}
I know its not much, but I need the exception object to be able to do some extra handling.
If you wish to execute different code based on the TYPE of the exception thrown, you should look at using #ControllerAdvice along with #ExceptionHandler. Any exceptions that you do not handle with an #ExceptionHandler will then bubble up to the default ErrorController (though you could handle Exception in a handler and then all exceptions will be handled via your custom handler). Something like:
#ControllerAdvice
#RestController
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(MyException.class)
protected ResponseEntity<String> handleMyException(MyException ex) {
// Your code here
}
#ExceptionHandler(Exception.class)
protected ResponseEntity<String> handleException(Exception ex){
// Your code here
}
}
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler
No worries guys, I solved the problem, I just had to do some digging into springs built in error controller to get the stacktrace.
I have a basic SpringBoot 2.0.4.RELEASE app. Using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file.
I have created this class to manage the exceptions
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
public RestResponseEntityExceptionHandler() {
super();
}
// API
// 400
...
}
But when I try to use it in 1 of my method :
#GetMapping(path = "/getUsers", consumes = "application/json", produces = "application/json")
#ExceptionHandler({RestResponseEntityExceptionHandler.class })
public ResponseEntity<List<User>> testErrors(HttpServletRequest request, #RequestHeader(value = "Authorization") String authHeader) {
...
}
I got this compilation error :
Type mismatch: cannot convert from Class to Class>
If you read the docs for #ExceptionHandler you will see that it is used to mark a method to handle exception(s). Therefore you have to specify which exception(s) it can handle (e.g. #ExceptionHandler(MyException.class) or #ExceptionHandler({MyException.class, MyOtherException.class})). When such an exception occurs in your controller this method gets invoked.
#ControllerAdvice makes the methods defined in the class available to all your controllers.
I don't know what you want to do, but since you extend ResponseEntityExceptionHandler in the #ControllerAdvice class, you could just delete the #ExceptionHandler annotation:
#GetMapping(...)
public ResponseEntity<List<User>> testErrors(...) {
...
}
If you want to handle specific exceptions on your own, you can do it like this:
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(MyException.class)
public ResponseEntity<?> handleMyException() {
...
}
}
#Controller
public class MyController {
#GetMapping(...)
public ResponseEntity<List<User>> testErrors(...) {
throw new MyException();
}
}
Now handleException() will be called when an error occurs in testErrors().
If you want to handle exceptions only in one controller you can do this:
#Controller
public class MyController {
#GetMapping(...)
public ResponseEntity<List<User>> testErrors(...) {
throw new MyException();
}
#ExceptionHandler(MyException.class)
public ResponseEntity<?> handleMyException() {
...
}
}
It's expected for this not to work correctly.
You're attempting to pass in the RestResponseEntityExceptionHandler as the arguments of the #ExceptionHandler annotation in your test method. This is wrong, as this annotation accepts the type of the exception that gets intercepted.
Also in general it seems that the placement of your #ExceptionHandler annotation seems to be wrong. This are placed within method that reside in the actual exception handling class.
I suggest you have a good read on the way Spring handles exceptions in that manner.
Have a look at this piece of documentation: exception handling in Spring.
I am trying to perform some common logic that applies to all my #ExceptionHandlers in code. I know I can write a HandlerInterceptor to intercept happy paths. But I would like to hook into the exception handling lifecycle so that I can execute some common logic such as logging, before the error response is rendered.
Is there anyway to do this in Spring Boot / Spring MVC? I would like to avoid having to write a servlet filter for this purpose if possible.
I have a solution. It's about using HandlerInterceptor.afterCompletion method. However, there is a line in the documentation of this method that states that:
Note: Will only be called if this interceptor's preHandle method has successfully completed and returned true!
So the trick is to also implement the preHandle and make it return true.
Now my interceptor looks like this:
#Component
public class MyInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// perform common logic here
}
}
One thing to be aware of though is that if you have a chain of interceptors and an interceptor before this one throws an exception, this interceptor won't get a chance to execute. So if we reorder the interceptor chain so that MyInterceptor is right at the top, it will intercept all requests.
There is a way with #RestControllerAdvice and #ExceptionHandler, an example:
#RestControllerAdvice
public class GlobalControllerExceptionHandler {
#ExceptionHandler(value = {DeniedPermissionException.class})
#ResponseStatus(HttpStatus.FORBIDDEN)
public String deniedPermissionException(DeniedPermissionException ex) {
return "Denied permission";
}
#ExceptionHandler(value = {ConstraintViolationException.class})
#ResponseStatus(HttpStatus.BAD_REQUEST)
public String constraintViolationException(ConstraintViolationException ex) {
return "Bad request";
}
#ExceptionHandler(value = {Exception.class})
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String internalServerError(Exception ex) {
return "Internal error";
}
}
*DeniedPermissionException is a custom exception.
This is not really a Spring Boot concern. This is really a Spring MVC concern. One good approach is to implement the HandlerExceptionResolver or extend from something like ExceptionHandlerExceptionResolver. The implementation needs to be given a higher precedence than the default exception resolvers (which all run with the lowest precedence). And if you want to retain the existing behavior of the default resolvers but only trap the exception for something cross-cutting like logging or tracking, then just return null for the ModelAndView and Spring will ensure other default resolvers are invoked as before.
Here,my requirement is that i want separate code in my application for exception handling,i saw a nice option of spring there using #controller advice to handle exceptions globally.
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.CONFLICT) // 409
#ExceptionHandler(DataIntegrityViolationException.class)
public void handleConflict() {
// Nothing to do
}
}
But there i want to cutomization there,like proper dynamic messages,own error code. so how can i do this,i am new to spring boot and even i don't have knowledge of spring.Need basic example.
You can come up with a class like this to capture information to be sent in response in case of exception:-
public class APIResponse {
int errorCode;
String description;
String someInformation;
// any other information that you want to send back in case of exception.
}
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.CONFLICT) // 409
#ResponseBody
#ExceptionHandler(DataIntegrityViolationException.class)
public APIResponse handleConflict(DataIntegrityViolationException exception) {
APIResponse response = createResponseFromException(exception);
return response;
}
}
In your controller advice class:-
Have the return type APIResponse instead of void.
The handler method can have the exception raised as the argument.
Using the exception object to create the APIResponse object.
Put #ResponseBody on the handler method.
I am going to introduce the global handler on my web application:
#ControllerAdvice
public class GlobalControllerExceptionHandler {
#ExceptionHandler(CustomRuntimeException.class)
public #ResponseBody ImmutableMap<?, String> handleNullResponseException(CustomRuntimeException e) {
return ImmutableMap.of(e.getClass(), e.getMessage());
}
}
But the issue is that legacy code contains a few controllers with local handlers like this:
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(Exception.class)
public #ResponseBody ExceptionDetails handleException(Exception e) {
return handleException(e);
}
And when controller throws CustomRuntimeException it handles by local one not global. In order to fix it I can add to each of these controllers local handlers similar to global. But as for me it is not a good one.
The question: Is it possible to redirect handling custom exceptions to the global handler?
You need to put more specific Exceptions in Local ExceptionHandler and more general Exceptions in Global ExceptionHandler. Similar to Java Exception handling. If you put the General one in Local, all Exceptions will end up there because it is the closest one and accepts any exception.
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(IOException.class)
public #ResponseBody ExceptionDetails handleIOException(IOException e) {
return handleException(e);
}
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(NullPointerException.class)
public #ResponseBody ExceptionDetails handleNPException(NullPointerException e) {
return handleException(e);
}
Spring exceptionHandler works kind of like the try and catch
When the controller has an exception and it has a local exception handler the request will be handled by the local exception handler. Now if it does not find local then we tries to look for global.
In your case local exceptionHandler handles all exception thus global exception handler not called.