Ours is a Spring MVC based REST application. I am trying to use ExceptionHandler annotation to handle all errors and exceptions.
I have
#ExceptionHandler(Throwable.class)
public #ResponseBody String handleErrors() {
return "error";
}
This works whenever there is an exception thrown and it doesn't work for any errors.
I am using Spring 4.0. Is there any work-around?
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.
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;
}
Related
Is there a way to catch all exceptions at once that are thrown from a web request in a Spring Boot Exception Handler? I know I can catch an array of Exception types in my method annotated with #ExceptionHandler but it's not the types I'm talking about. I need something like a list of Exception objects. I already tried
##ExceptionHandler({ MethodArgumentTypeMismatchException.class, ConstraintViolationException.class })
#ResponseBody
private Error handleException(final List<Exception> ex, WebRequest request) {
...
}
but Spring is not able to find a suitable resolver for that:
java.lang.IllegalStateException: Could not resolve parameter [0] in private com.example.demo.model.Error com.example.demo.exception.MyExceptionHandler.handleException(java.util.List<java.lang.Exception>,org.springframework.web.context.request.WebRequest): No suitable resolver
With catching only one Throwable object it works fine:
#ExceptionHandler({ MethodArgumentTypeMismatchException.class, ConstraintViolationException.class })
#ResponseBody
private Error handleException(final Exception ex, WebRequest request) {
...
}
But what to do if I have different parameter violations like e.g. a ConstraintViolationException and a MethodArgumentTypeMismatchException in the same request?
If it's not possible to process a list of exceptions, how can I satisfy RFC-7807 (see https://www.rfc-editor.org/rfc/rfc7807)? Which means: How can I collect all invalid parameters, no matter what's the causing exception?
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler
#ExceptionHandler
public ResponseEntity<String> handle(Exception ex) {
// ...
}
you will catch most general exception. Then you can get suppressed
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Throwable.html#getSuppressed()
you cant throw more than one exception at once
I don't know about the annotation, but to catch multiple exceptions you do (remember that the first matching catch block will execute):
#ResponseBody
public MyError handleException(List<Throwable> exceptions, WebRequest request) {
try {
//...
} catch (ConstraintViolationException e) {
//...
} catch (MethodArgumentTypeMismatchException e) {
//...
}
}
My logic was to implement a global exception filter which handles any exception inside my Spring MVC, and also has #ControllerAdvice to Handle exception
Global Filter
#Component
public class GlobalExceptionHandlerFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (Throwable ex) {
...
}
}
}
Controller Advice
#ControllerAdvice
public class BaseController {
#ExceptionHandler(value = {ConstraintViolationException.class})
public void handlePersistenceException(ConstraintViolationException ex, HttpServletRequest request) throws MyException {
String str = "";
for (ConstraintViolation constraintViolation : ex.getConstraintViolations()) {
str += "Property '" + constraintViolation.getPropertyPath() + "' - " + constraintViolation.getMessage();
}
MyException myException = new MyException(str);
throw myException;
}
}
The globalfilter wraps the execution of the exception so it captures the ConstraintViolation.
But my logic needs to modify the ConstraintViolation as MyException and throw it from ControllerAdvice. The global still captures the ConstraintViolation, not the MyException which is thrown from ControllerAdvice, but the exception is not carried over to the filter when I am not throwing MyException.
How to override the ConstraintViolationException with MyException in ControllerAdvice to make it captured by the globalexception filter.
Wrap your MyException class in ResponseEntity and return it with the required HttpStatus.
#ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<MyException> notFoundException(HttpServletRequest request,
HttpMessageNotReadableException e) {
MyException myException = new MyException("CustomMessage");
logger.error("An constrain voilation occured reason {}", e);
return new ResponseEntity<>(myException , HttpStatus.BAD_REQUEST);
}
You cannot achieve the above required scenario, as the ExceptionHandler invoker(ExceptionHandlerExceptionResolver.doResolveHandlerMethodException) which invokes the method binded with the perfect match for the exception catches any exception from the invoked method.
The default action in this chain incase of exception in handler is to rethrow the original Exception which invoked the handler.
I am working in a spring mvc based project and have developed a ExceptionResolver by extending DefaultHandlerExceptionResolver to redirect to error page depending on the exception type. It is working for exception raised at facade, service, DAO layer.
But it doesn't work for any exceptions raised in Servlet filter. What changes should be made for that?
Below is my handlerExceptionResolver
public ModelAndView doResolveException(final HttpServletRequest request, final HttpServletResponse response, final Object obj,
final Exception exception){
ModelAndView modelAndView = super.doResolveException(request, response, obj, exception);
modelAndView = Objects.nonNull(modelAndView) ? modelAndView : new ModelAndView();
final String url = Config.getParameter(EXCEPTION_HANDLER_URL);
modelAndView.setViewName(url);
final FlashMap outputFlashMap = RequestContextUtils.getOutputFlashMap(request);
outputFlashMap.put(ERROR_DETAILS, exception);
if (exception instanceof BusinessExecutionException)
{
return handleBusinessExecutionExceptionMethod((BusinessExecutionException) exception, outputFlashMap, modelAndView);
}
else if (exception instanceof IntegrationExecutionException)
{
return handleIntegrationExecutionExceptionMethod((IntegrationExecutionException) exception, outputFlashMap,
modelAndView);
}
else if (exception instanceof DataAccessObjectExecutionException)
{
return handleDAOExecutionExceptionMethod((DataAccessObjectExecutionException) exception, outputFlashMap, modelAndView);
}
return handleMiscException(exception, outputFlashMap, modelAndView);
}
Use Spring exception handler:
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
It is convenient - you can "catch" exception regarding type and HTTP status.
I have a #ControllerAdvice class to handle exceptions from my SpringMVC controllers. I would like to catch an exception of a known type (RuntimeException) in an #ExceptionHandler method then throw the e.getCause() exception and have this exception caught by the same #ControllerAdvice class.
Sample code:
#ControllerAdvice
public class ExceptionHandlingAdvice
{
#ExceptionHandler( RuntimeException.class )
private void handleRuntimeException( final RuntimeException e, final HttpServletResponse response ) throws Throwable
{
throw e.getCause(); // Can be of many types
}
// I want any Exception1 exception thrown by the above handler to be caught in this handler
#ExceptionHandler( Exception1.class )
private void handleAnException( final Exception1 e, final HttpServletResponse response ) throws Throwable
{
// handle exception
}
}
Is this possible?
You can check if that RuntimeException is instance of Exception1.class and call the method directly:
private void handleRuntimeException( final RuntimeException e, final HttpServletResponse response ) throws Throwable
{
if (e instanceof Exception1) handleAnException(e,response);
else throw e.getCause(); // Can be of many types
}
Few years late on this.. but just ran into a need for this in dealing with #Async services - when throwing an exception, they get wrapped in the ExecutionException.class and wanted my controller advice to direct them to their proper handler, an identical situation you were in.
Using reflection, can gather all the methods on the controller advice, sift for the matching #ExceptionHandler annotation for e.getCause().getClass() then invoke the first found method.
#ControllerAdvice
public class ExceptionHandlingAdvice
{
#ExceptionHandler( RuntimeException.class )
private void handleRuntimeException( final RuntimeException e, final HttpServletResponse response )
{
if (e.getCause() != null) {
Optional<Method> method = Arrays.stream(Rest.Advice.class.getMethods())
.filter(m -> {
// Get annotation
ExceptionHandler annotation = m.getAnnotation(ExceptionHandler.class);
// Annotation exists on method and contains cause class
return annotation != null && Arrays.asList(annotation.value()).contains(e.getCause().getClass());
})
.findFirst();
if (method.isPresent()) {
try {
method.get().invoke(this, e.getCause(), response);
} catch (IllegalAccessException | InvocationTargetException ex) {
// Heard you like exceptions on your exceptions while excepting
ex.printStackTrace();
}
}
}
// Handle if not sent to another
}
... other handlers
}
Didn't test with void -- Personally, I return ResponseEntity<MyStandardErrorResponse> from my handlers, so my invoke line looks like:
return (ResponseEntity<MyStandardErrorResponse>) method.get().invoke(this, e.getCause(), request);
Ours is a Spring MVC based REST application. I am trying to use ExceptionHandler annotation to handle all errors and exceptions.
I have
#ExceptionHandler(Throwable.class)
public #ResponseBody String handleErrors() {
return "error";
}
This works whenever there is an exception thrown and it doesn't work for any errors.
I am using Spring 4.0. Is there any work-around?
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.
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;
}