I have a Spring MVC controller with some simple REST service requests. I would like to add some error handling when specific exceptions are thrown from my services, but I cannot get a handler method annotated with #ExceptionHandler to actually ever be called. Here is one service I am deliberately throwing an exception to try and get my handler method to take over. The handler method is never invoked and Spring just returns a 500 error to the calling client. Do you have any ideas on what I'm doing wrong?
#ExceptionHandler(IOException.class)
public ModelAndView handleIOException(IOException ex, HttpServletRequest request, HttpServletResponse response) {
response.sendError(HttpServletResponse.SC_FORBIDDEN);
System.out.println("It worked!");
return new ModelAndView();
}
#RequestMapping(value = "/json/remove-service/{id}", method = RequestMethod.DELETE)
public void remove(#PathVariable("id") Long id) throws IOException {
throw new IOException("The handler should take over from here!");
}
Frustratingly, I have suffered from this as well. I discovered that if you mistakenly implement Throwable instead of Exception the Exception resolver will just rethrow your Throwable as a IllegalStateException. This will fail to invoke your #ExceptionHandler.
If you've implemented Throwable instead of Exception try changing it to Exception instead.
Here's the code in question from InvocableHandlerMethod
catch (InvocationTargetException e) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = e.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
throw new IllegalStateException(msg, targetException);
}
}
This tip on the Spring forum may help you.
Likely you have configured the beans for your DispatchServlet in a webmvc-servlet.xml file (the *-servlet.xml file may be named differently)
If the XML file already includes another ExceptionResolver (like SimpleMappingExceptionResovler Spring wont automatically add any other resolvers for you. So manually adding the annotation resolver like so:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver" />
Should enable the #HandlerException processing.
I have found that #ExceptionHandler works with Throwable when you define method in such a manner:
#ExceptionHandler(Throwable.class)
#ResponseBody
public String handleException(Throwable e) {
}
In this case method has only one argument of type Throwable. If I try to use in this method some additional parameters (I tried to use Model), I receive 500 exception (this method isn't call). However this still works when additional parameters are HttpServlerRequest or HttpServlerResponse.
It will not work, because when you return String you return View name.
Now your Spring MVC controller is searching for This method is never called! Why not?! view and can find.
Make sure that #ExceptionHandler is mapped to existing view.
Related
I have a use case where for certain exception's that are normally thrown by a web framework that I can override the value by using my own MyCustomException object.
#ExceptionHandler({SomeWebFrameworkException.class})
public ResponseEntity<Object> handleException(MyCustomException exception) { ... }
However, if I want an exception handler to be able to be able to accept my custom exception then, I would need to cover all cases of this web framework error being thrown. Is there a way to somehow make it accept MyCustomException as input otherwise just default to a normal Exception ? If I just use a simple Exception as the input then, it would end up getting treated as the SomeWebFrameworkException instead of my own.
#ExceptionHandler({SomeWebFrameworkException.class})
public ResponseEntity<Object> handleException(Exception exception1, MyCustomException exception2) { ... }
You can have multiple exception handling methods defined, similarly to catch-blocks
#ExceptionHandler(MyCustomException.class)
public ResponseEntity<Object> handleMyCustomException(MyCustomException exception) { ... }
#ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleGenericException(Exception exception) { ... }
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) {
//...
}
}
How to get all exception handlers annotated by #ExceptionHanlder and I can call them manually?
Background
I need to handle some exceptions by my own exception handlers but in some situation my handled exceptions are not thrown directly by spring, and they are wrapped in the cause by. So I need to handle these caused by exceptions in one place using my own exception handling strategy in the existing #ExceptionHandlers. How can I do that?
Try to use Java Reflection Api to find classes annotated with "ExceptionHanlder". And invoke any method or whatever you want.
You can extend ResponseEntityExceptionHandler and make it a #ControllerAdvise like below.
#ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler({YourException.class})
public ResponseEntity<Object> handleMyException(Exception ex, WebRequest request) {
... handle the way you like it
return new ResponseEntity<Object>(YourErrorObject, new HttpHeaders(), HttpStatus);
}
}
Spring provides #ControllerAdvice annotation that we can use with any class to define our global exception handler. The handler methods in Global Controller Advice is same as Controller based exception handler methods and used when controller class is not able to handle the exception.
You want to use exception handling strategy in your one place. that you can define multiple exception or make message using exception in exception controller.
like this :
#ExceptionHandler(value = { HttpClientErrorException.class, HTTPException.class, HttpMediaTypeException.class,
HttpMediaTypeNotSupportedException.class, HttpMessageNotReadableException.class })
or
#ExceptionHandler
#ResponseBody
ExceptionRepresentation handle(Exception exception) {
ExceptionRepresentation body = new ExceptionRepresentation(exception.getLocalizedMessage());
HttpStatus responseStatus = resolveAnnotatedResponseStatus(exception);
return new ResponseEntity<ExceptionRepresentation>(body, responseStatus);
}
HttpStatus resolveAnnotatedResponseStatus(Exception exception) {
ResponseStatus annotation = findMergedAnnotation(exception.getClass(), ResponseStatus.class);
if (annotation != null) {
return annotation.value();
}
return HttpStatus.INTERNAL_SERVER_ERROR;
}
Here is a work around. You can catch the the wrapping exception and then check the root cause of the exception. Here is an example of MySQLIntegrityConstraintViolationException which is wrapped by DataIntegrityViolationException in spring:
#ExceptionHandler(DataIntegrityViolationException.class)
#ResponseBody
public ResponseEntity<Object> proccessMySQLIntegrityConstraint(DataIntegrityViolationException exception) {
if (exception.getRootCause() instanceof MySQLIntegrityConstraintViolationException) {
doSomething....
} else {
doSomethingElse...
}
}
I wrote a custom exception with Spring 5 reactive
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class AddressNotFoundException extends RuntimeException{
public AddressNotFoundException(String message) {
super(message);
}
and I call this one in a service:
#Override
public Mono<Address> getById(String id) {
Address addressFound=repository.findById(id).block();
if(Objects.equals(addressFound, null))
throw new AddressNotFoundException("Address #"+id+" not found");
return Mono.just
(addressFound);
}
but when I reach this page an exception is thrown but it's not a 404 but a null pointer exception and a error 500 page but with the correct message ?
The AddressNotFound is never thrown, only the Nullpointer exception but with my custom message ???
Can you help me please ?
Here is my controller :
#GetMapping("/address/{id}")
public Mono<Address> byId(#PathVariable String id) {
return addressService.getById(id);
}
Thanks
if your address is null,
repository.findById(id).block();
Should be throwing the NullPointerException I suppose. In that way it could never reach the code line to throw the custom exception.
Instead of extending RuntimeException try extending just generic Exception.
Spring offers a ControllerAdvice annotation to intercept exceptions that are thrown
#ControllerAdvice
public class ExceptionController extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = { AddressNotFoundException.class })
protected ResponseEntity<Object> handleAddressNotFoundException(Exception ex, WebRequest request) {
AddressNotFoundException notFound = (AddressNotFoundException)ex;
return handleExceptionInternal(ex, String.valueOf(notFound), new HttpHeaders(), HttpStatus.NOT_FOUND, request);
}
}
This will send the error back to the client as a 404 in which you can digest on the client side to display to the user typically in the form of a json string. Can either override the exceptions toString method to return it as a json or write a helper method that would do that.
You probably have a HandlerExceptionResolver bean which is causing 500 for some reason. Please try taking that off for a while.
I've tried with spring boot 1.5 it works with Spring Boot 2 without webflux it works, so it seems that Webflux can't handle custom exception ???
I have a Spring MVC controller throw two kind of exception:
#RequestMapping(value = "forwardRefundApply",method = RequestMethod.GET)
public ModelAndView forwardRefundApply1(String ticketNbr)throws Exception {
if(true)
throw new Exception();
else
throw new ApplicationException("test");
}
then I write a AOP class to hanlder the Exception then return Model like this:
#Pointcut("execution(public * ..*(..))")
public void getRefundPointCut() {
}
#AfterThrowing(pointcut="getRefundPointCut()", throwing="e")
public ModelAndView throwException(Exception e){
ModelAndView mav = null;
if(e instanceof ApplicationException)
{
e.printStackTrace();
mav = new ModelAndView(CommonConstants.ERRORPAGE);
mav.addObject("errorMsg", "application error");
return mav;
}
else{
e.printStackTrace();
mav = new ModelAndView(CommonConstants.ERRORPAGE);
mav.addObject("errorMsg", "system error");
return mav;
}
}
the aop is work . but the the result is error. system error:
org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.NoSuchMethodError
is Aspect class cannot return ModelAndView to Controller?
Why use AOP in this case at all? Spring comes with everything you need:
HandlerExceptionResolver is a global entry point into exception handling.
#ExceptionHandler lets you define handlers in controllers.
#ControllerAdvice lets you define #ExceptionHandlers on a global level.
The NoSuchMethodError is unrelated to your question and is caused by something you haven't shown us.
As for the question
is Aspect class cannot return ModelAndView to Controller?
I couldn't find any reference to it anywhere in Spring's AOP documentation, but you can see it in the implementation.
When you declare a #AfterThrowing advice, Spring uses a AspectJAfterThrowingAdvice to handle it. Its invoke(..) method is implemented as
#Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable t) {
if (shouldInvokeOnThrowing(t)) {
invokeAdviceMethod(getJoinPointMatch(), null, t);
}
throw t;
}
}
where mi.proceed() invokes your advised method and invokeAdviceMethod(..) invokes your #AfterThrowing advice method. Notice that it does nothing with the return value. As such, you can return a ModelAndView object from a #AfterThrowing advice method, but it won't serve any purpose, it'll simply be discarded.
A possible alternative is to declare a #Around advice. Within it, you wrap the proceeding call and catch the possible exceptions, handling them appropriately
#Around(value = "getRefundPointCut()")
public ModelAndView throwException(ProceedingJoinPoint joinPoint) throws Throwable {
ModelAndView mav = null;
try {
return (ModelAndView) joinPoint.proceed(); // might want to make sure that it is a ModelAndView
} catch(ApplicationException e) {
e.printStackTrace();
mav = new ModelAndView("home");
mav.addObject("errorMsg", "application error");
return mav;
} catch (Exception e) {
e.printStackTrace();
mav = new ModelAndView("home");
mav.addObject("errorMsg", "system error");
return mav;
}
}
Here you return the value of the advised method if it returns correctly. Or your catch any thrown Exception and again handle it appropriately by returning a different ModelAndView.
I haven't tested it myself but Spring AOP works with proxy objects and not the actual objects. So e is actually an instance of proxy and not of ApplicationException. So the following condition never executes to true.
if(e instanceof ApplicationException)
Easiest way of handling this would be to mark your aop setting in Spring configuration file with proxy-target-class="true".
<aop:aspectj-autoproxy proxy-target-class="true"/>
HTH