Reputation: 173
I have @ControllerAdvice
class, which handles a set of exceptions. Than we have some other exceptions, which are annotated with @ResponseStatus
annotation. To combine both approaches, we use technique described in blog post: http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc, namely in the ControllerAdvice
we handle generic Exception
in the following way:
@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
// If the exception is annotated with @ResponseStatus rethrow it and let
// the framework handle it - like the OrderNotFoundException example
// at the start of this post.
// AnnotationUtils is a Spring Framework utility class.
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
throw e;
// Otherwise setup and send the user to a default error-view.
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
It works like a charm, however, using this technique causes error with the following text to appear in the application log:
2014-06-11 15:51:32.907 ERROR o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Failed to invoke @ExceptionHandler method: ...
This is caused by this piece of code in ExceptionHandlerExceptionResolver
:
try {
if (logger.isDebugEnabled()) {
logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);
}
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception);
}
catch (Exception invocationEx) {
logger.error("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
return null;
}
Does anybody know how to combine these two approaches to exception handling properly to avoid the error in the log?
Thanks, Jan
Upvotes: 10
Views: 7353
Reputation: 72
I had the same problem when I missed this import
:
import org.springframework.http.HttpStatus;
Eclipse didn't offer me to add it in Quick Fix, I added the row manually and it helped.
Upvotes: 1
Reputation: 513
This is an old question, but I just encountered this today and found a better solution than disabling logging for ExceptionHandlerExceptionResolver
. Turns out that the issue can be resolved by upgrading to the latest version of the spring framework (4.3.8 worked for me). The ExceptionHandlerExceptionResolver
has been fixed to detect if the original exception was rethrown from an @ExceptionHandler
. In this case, the exception is no longer logged.
Upvotes: 3
Reputation: 406
I dealt with it in a slightly different way which I think solves your problem.
Since I know that basically I want to deal with 404's differently to 500s of whatever hue, I look for a NOT_FOUND status and send that accordingly, which seems to work and then you're not re-throwing the exception.
This means
@ControllerAdvice
public class MVCExceptionHandler {
private static final Logger log = LogManager.getLogger();
@ExceptionHandler(Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, HttpServletResponse res, Exception ex) throws Exception {
// If the exception is annotated with @ResponseStatus check if it's a 404 in which case deal with it, otherwise 500 it.
if (AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class) != null) {
ResponseStatus rs = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
if (HttpStatus.NOT_FOUND.equals(rs.value())) {
res.setStatus(HttpStatus.NOT_FOUND.value());
return new ModelAndView("error/404");
}
}
log.error("Error while loading", ex);
return new ModelAndView("error/500");
}
}
Upvotes: 1
Reputation: 2274
The problem is that you're throwing an exception rather than returning a ModelAndView
. Essentially, you are trying to re-implement ResponseStatusExceptionResolver
in your ControllerAdvice
class. You could do that (e.g. by copy/pasting code from ResponseStatusExceptionResolver
) but aside from being duplication, that approach would also be incomplete. Spring also comes with DefaultHandlerExceptionResolver
for some built-in exceptions, which this approach may not handle correctly.
Instead, what I have done is not have a catch-all handler for Exception.class
in my ControllerAdvice
class. I don't know if this is the best solution, but it's the best I know of.
Upvotes: 0