I'm using Spring 3.2.1 and have a PropertyEditorSupport to force a parameter I'd like autoconverted to an enum to uppercase so it converts wrong-cased parameters. An IllegalArgumentException can be thrown if the parameter String can't be converted to the enum. My problem is that the status code is returned as 500 when it ought to be 400. I created a ResponseEntityExceptionHandler annotated with #ControllerAdvice and with a method annotated #ExceptionHandler (IllegalArgumentException.class), intending to set the status code there, but the method is never called when the exception is thrown. Why not? Is it the same problem as discussed here, MaxUploadSizeExceededException doesn't invoke the exception handling method in Spring ? i.e.
"...the exception is thrown before the request has reached the
dispatcher servlet. Therefore your exceptionhandler isn't called
because at the point the exception is thrown the target controller has
yet to be determined."
If so how can I return the proper status code?
Short version:
Put #ExceptionHandler(Exception) annotated method in #Controller class.
Longer version:
I noticed that the #ExceptionHandler(XxxException.class) annotated method in #ControllerAdvice class will only be called if XxxException happens "after" entering the method with matching #RequestMapping.
Conditions like MaxUploadSizeExceededException and MethodArgumentNotValidException will cause SpringMVC to NOT enter the #RequestMapping method, so advice is not applied.
My solution was to sadly place #ExceptionHandler(XxxException.class) annotated method in the actual controller class. Not nice but works.
Hope this helps. If someone knows of a better solution (when using #ControllerAdvice), do share.
Related
I would like some advice on how to achieve the following. I'm not providing code, as my problem is theoretical, but upon request I can. So this is the situation:
I have multiple controllers, each can throw XYException
I have a #ControllerAdvice class, in which I have an #ExceptionHandler watching for XYExceptions. When it happens, it prints out "XY".
In one (and only one) controller, when XYException is thrown, I want to do some additional task (let's say, do something that only that controller can do), and then I want the exception to be "passed on" to the global handler mentioned above.
I know I can catch the exception, do the desired task in catch block, and then re-throw the exception so the global handler can catch it, but what if I have 23 methods in the controller potentially throwing XYExceptions, and I do not want to put try-catch blocks in all 23 methods.
What is the clean Spring way of achieving this?
You could use AOP to achieve that. You'd write a method that intercepts the method inside that controller and when they throw an exception, you're aop method would run and you can do your stuff and then the exception would go in your handler class.
To achieve that you should have a class and anotate it with #Aspect and #Component
then have a method anotated with #AfterThrowing and setting the pointcut which will intercept when the given method throws an exception.
Look into Aspect Oriented Programming and Aspectj for more info.
The easy way to handle this case in ControllerAdvice is checking the stacktrace where the exception originated.
#ExceptionHandler(Exception.class)
public String handleExc(HttpServletRequest req, HttpServletResponse res, Exception e) {
if ( /*Have all null and safe check */ e.getStackTrace()[0].contains("MyController")) {
// Do your exception handling
}
}
I have come across few Spring contoller's function, which are throwing IOException.
#RequestMapping(method = ***)
#ResponseBody
public List<Offering> getOfferingDetailsList(HttpServletResponse response, #PathVariable("productIds") String productIdString, HttpServletRequest request) throws IOException
I doubt about use of such exception throwing, when no one above is catching and handling it. Is it fine to set response status like "response.setStatus(HttpStatus.SC_NOT_FOUND)" in place of throwing such exception ? What is the standard way of handling exception in controller ?
it is always good to have common exception handling functionality , so that we can make our controller code free from exception handling , by externalize to common exception handling functionality, i have come across this interesting topic for this purpose
http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
and also use a global exception handler that will do that for all the exceptions of all the controller methods. That will centralize the exception handling, prevent massive code duplication, and avoid cluttering your controller methods.
Look at the #ControllerAdvice and #ExceptionHandler annotations.
A fairly standard way of handling exceptions in Spring Controllers is to use #ExceptionHandler.
Check out this post for more details
If I have a Spring Controller with two SEPARATE methods, one annotated by:
#ExceptionHandler(Exception.class)
and another annotated by:
#ExceptionHandler(SubException.class)
And my controller throws SubException.class, does it get handled by both methods, or just #ExceptionHandler(SubException.class)?
One handler will be invoked on a best fit basis.
The exact implementation is in AnnotationMethodHandlerExceptionResolver.findBestExceptionHandlerMethod(Object,Exception)
You can create your own annotation class which act as exception. And after that you need to provide your class annotation instead of Exception-handler.
Please let me know, if you have any query.
Handlers typically implement Spring’s Ordered interface so you can define the order that the handlers run in.
see Exception Handling in Spring MVC
I can't figure out how to handle more than one kind of exception by #ExceptionHandler.
I need to programmatically deal with these exceptions, for this I'd need a shared reference. Is this done via this reference "Exception ex" ? I don't think so, cause the exception is not caught like this, how would I do it then ?
I can't put all the exception references as arguments to the handler method, it wouldn't make sense, it can't be programmatically dealt with. I need a shared reference so that I could use "instanceof" on it or just send it somewhere else as a general "Exception"
#ExceptionHandler({DescriptionCstOrderException.class, SpecializationCstOrderException.class, NoUploadFileException.class,
DeadLineCstOrderException.class, DocumentCstOrderException.class, CommentCstOrderException.class})
public String handleFormException(Exception ex, ActionRequest actionRequest) {
logger.error(ex.getMessage());
SessionErrors.add(actionRequest, ex.getClass().getName());
return "mainOrderForm";
}
Additional question: what if I wanted to handle org.springframework.web.multipart.MaxUploadSizeExceededException, that is not thrown from any method of the handler? Because #ExceptionHandler catches only exceptions that are thrown from one of the handler methods.
The exceptionHandler method could be placed into some extended parent controller but if one uses only defaultAnnotationHandlerMapping... ?
Appreciate any help, I'm going crazy, this is very frustrating....
The #ExceptionHandler value can be set to an array of Exception types.
The implementation of using exception array as mentioned in Spring documentation will be like:
#ExceptionHandler({
NotFoundException.class,
MissingServletRequestParameterException.class
})
The #ExceptionHandler value can be set to an array of Exception types. If an exception is thrown matches one of the types in the list, then the method annotated with the matching #ExceptionHandler will be invoked. If the annotation value is not set then the exception types listed as method arguments are used. See the documentation for details.
Your question is rather confusing but your exception handler method will only handle one exception at a time. It will not catch multiple exceptions and then pass both of them into your handleFormException() method. If you need to handle these exception types differently then you should create an exception handler method for each one, specify an argument of that specific Exception type to your method, and then do the appropriate handling. For example:
#ExceptionHandler(DescriptionCstOrderException.class)
public String handleDescriptionCstOrderException(DescriptionCstOrderException exception, ActionRequest actionRequest) {...}
#ExceptionHandler(SpecializationCstOrderException.class)
public String handleSpecializationCstOrderException(SpecializationCstOrderException exception, ActionRequest actionRequest) {...}
// and so on...
Please refer to the Spring documentation for further information:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-exceptionhandler
I am using Spring with Velocity. At times velocity produces an exception like
Error in interpolating string literal : org.apache.velocity.exception.MethodInvocationException: Invocation of method 'getMessage' in class org.springframework.web.servlet.support.RequestContext threw exception class org.springframework.context.NoSuchMessageException : No message found under code 'XX' for locale 'en_US'.
Question is - can I instruct spring to suppress NoSuchMessageException ? I am pretty new to Spring so do not know if I can create a exception handler which will not throw a exception if the message is not found? In my use case, it is a valid use case of not finding some of the messages in the messages.properties file.
[EDIT] - I found a way org.apache.velocity.app.event.MethodExceptionEventHandler to supply an even handler to velocity. I am however not sure how to register it with Spring.
It would be better, I think, to address the problem directly, rather than trying to suppress the exception, which could lead to avoid behaviour and uncompleted operations.
However, you didn't tell us how you'd want the system to respond in cases where the message is not defined for a given code. Do you want a blank String, or should the code itself be used as the default message?
I'm assuming that somewhere in your Spring context you have a defined a messageSource bean of some sort. These MessageSource beans usually have a property useCodeAsDefaultMessage. This defaults to false, but when set to true they will return the code itself as the message, in cases where no message is defined.
Alternatively, you can subclass the MessageSource class (whichever one you're using), and override the getDefaultMessage() method to handle cases where the code cannot be resolved.