Ours is a Spring MVC based REST application. I am trying to use ExceptionHandler annotation to handle all errors and exceptions.
I have
@Excepti
You can do a kind of Hacking to capture Error in Spring MVC. First, define an Interceptor like this :
public class ErrorHandlingInterceptor extends HandlerInterceptorAdapter {
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
{
super.afterCompletion(request, response, handler, ex);
controller.handleError(ex.getCause(), request, response);
} }
Second, define a method in your controller like "handleError" method:
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setExceptionId(exceptionId);
errorResponse.setErrorMsg(ex.toString());
errorResponse.setServerStackTrace(serverStackTrace(ex));
response.setStatus(responseCode);
response.setContentType("application/json");
ObjectWriter writer = mapper.writer().withDefaultPrettyPrinter();
writer.writeValue(response.getOutputStream(), errorResponse);
Finally, config your interceptor in Spring configuration.
<mvc:interceptors>
<bean class="ErrorHandlingInterceptor" />
</mvc:interceptors>
Code in DispatchServlet:
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
// This is where to handle Exception by Spring.
// If Error happens, it will go to catch Error statement
// which will call afterCompletion method
mv = processHandlerException(processedRequest, response, handler, ex);
errorView = (mv != null);
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
// Trigger after-completion for successful outcome.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}
catch (Exception ex) {
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
catch (Error err) {
ServletException ex = new NestedServletException("Handler processing failed", err);
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
Contrary to what the ExceptionHandler#value()
attribute indicates
Class<? extends Throwable>[] value() default {};
and @ExceptionHandler
is only meant to handle Exception
and its sub types.
Spring uses ExceptionHandlerExceptionResolver
to resolve your annotated handlers, using the following method
doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod, Exception exception)
which as you can see only accepts an Exception
.
You cannot handle Throwable
or Error
types with @ExceptionHandler
with this configuration.
I would tell you to provide your own HandlerExceptionResolver
implementation which does handle Throwable
instances, but you'd need to provide your own DispatcherServlet
(and most of the MVC stack) yourself since DispatcherServlet
does not catch
Throwable
instances at any place where you could make any significant difference.
Update:
Since 4.3, Spring MVC wraps a thrown Throwable
value in a NestedServletException
instance and exposes that to the ExceptionHandlerExceptionResolver
.