Today we’ll dig into the exception handling mechanism of Spring MVC 3.1
All the code mentioned in this article is based on Spring MVC 3.1. If you are using previous version of Spring MVC some assertions may not hold.
Please note that all the examples in this post can be found in a demo application on GitHub https://github.com/doanduyhai/SpringMVCExceptionGHandling
I Exception handling infrastructure
A Main classes
To manage exceptions, Spring MVC 3.1 offers by default the following classes:
- ExceptionHandlerExceptionResolver: generic exception handler
- DefaultHandlerExceptionResolver: exception handler supporting a set of predefined exceptions
- SimpleMappingExceptionResolver: good old exception handler (available since 2003!) to map a custom exception to a specific error page
It is possible to use those 3 handlers, all we need to do is to declare a HandlerExceptionResolverComposite which is basically a container of Exception handlers and delegates the exception handling to each of the registered handler.
<bean id="compositeExceptionResolver" class="org.springframework.web.servlet.handler.HandlerExceptionResolverComposite"> <property name="exceptionResolvers"> <list> <bean class="exceptionHandler1"/> <bean class="exceptionHandler2"/> <bean class="..."/> </list> </property> <property name="order" value="0"/> </bean>
With this configuration, each exception handler wil be invoked with respect to their declaration order (a list is ordered by nature)
B Detailed description
1) ExceptionHandlerExceptionResolver
This exception handler is the default handler and covers 80% of the use cases. On startup the class is registered in the Spring context with a set of default method argument resolvers and method return values handlers:
Default argument resolvers
- ServletRequestMethodArgumentResolver: supports the following types as method arguments
- WebRequest
- ServletRequest
- MultipartRequest
- HttpSession
- Principal (for security)
- Locale
- InputStream
- Reader
- ServletResponseMethodArgumentResolver: supports the following types as method arguments
- ServletResponse
- OutputStream
- Writer
In addition to these method arguments, this handler also handle the following return types and method annotations:
- Return types:
- ModelAndView
- Model
- View
- String: simple view name return
- Map
- HttpEntity: to customize Http request body as well as header
- Annotations:
- @ModelAttribute
- @RequestBody
- @ResponseBody
2) DefaultHandlerExceptionResolver
This class simply handles the following default exceptions:
Exception | Http Code |
NoSuchRequestHandlingMethodException | 404 (Not Found) |
HttpRequestMethodNotSupportedException | 405 (Method Not Allowed) |
HttpMediaTypeNotSupportedException | 415 (Unsupported Media Type) |
MissingServletRequestParameterException | 400 (Bad Request) |
ServletRequestBindingException | 400 (Bad Request) |
ConversionNotSupportedException | 500 (Internal Server Error) |
TypeMismatchException | 400 (Bad Request) |
HttpMessageNotReadableException | 400 (Bad Request) |
HttpMessageNotWritableException | 500 (Internal Server Error) |
MethodArgumentNotValidException | 400 (Bad Request) |
MissingServletRequestPartException | 400 (Bad Request) |
You need not declare any method with @ExceptionHandler for the above exceptions, they will be handled automatically by the class.
3) SimpleMappingExceptionResolver
This class lets you map an exception to an view. Simply define its “exceptionMappings” property by providing a list of exception/view pair values:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="java.lang.ClassNotFoundException">pages/classNotFoundException</prop> <prop key="java.lang.CloneNotSupportedException">pages/cloneNotSupportedException</prop> </props> </property> </bean>
Above, we map the ClassNotFoundException to the view “pages/classNotFoundException” and CloneNotSupportedException to “pages/cloneNotSupportedException“. Of course you must ensure that the view declaration is correct and the actual pages exist.
II Exception handling strategies
In this chapter we discuss about various strategies to handle exceptions.
A Return an error message to be displayed
This is the simplest use case for exception handling:
@RequestMapping(value = "rest/exception1") public String exception1() { throw new NullPointerException("Exception1 as plain text with <strong>html</strong> tags"); } @ExceptionHandler(NullPointerException.class) @ResponseBody public String handleException1(NullPointerException ex) { return ex.getMessage(); }
Above, on NullPointerException detection, we return the source message. Since the handler method is annotated with @ResponseBody, the source message will be simply put in the response body.
On the client side, some Javascript to display an error panel with the message:
function exception1() { $.ajax({ type: 'GET', url: "rest/exception1", success: function(data) { $('#messagePanel').empty().html(data).show(); } }); return false; }
Please notice that the returned exception message is handled by a success function (line 6) in Javascript. Indeed even if an Exception occured at the server side, as long as the Http response status is 200 the client side will consider it as a success.
B Return a dedicated error page
This strategy simply returns a generic error page and hides all the exception details from the end user.
@RequestMapping(value = "http/exception2", method = RequestMethod.GET) public String exception2() { throw new IndexOutOfBoundsException(); } @ExceptionHandler(IndexOutOfBoundsException.class) public String handleException2(IndexOutOfBoundsException ex) { return "pages/errorPage"; }
Please note that there is no redirect, we simply render a generic error page instead of the normal target page.
C Return an error code with custom message
In this strategy, we let Spring build an generic error page based on the Http code we provide with a custom message.
@RequestMapping(value = "http/exception3", method = RequestMethod.GET) public String exception3() { throw new IllegalStateException("Exception3 with response status"); } @ExceptionHandler(IllegalStateException.class) @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR, reason = "exception3") public void handleException3(IllegalStateException ex, HttpServletResponse response) throws IOException { }
Please note the definition of Http code and custom error message with the @ResponseStatus annotation at line 7. This strategy is quite equivalent to defining the error page at servlet level in the web.xml:
<error-page> <error-code>404</error-code> <location>/pages/404.html</location> </error-page>
D Redirect to a custom page with custom error message
In this strategy, we do a real HTTP redirect to a custom error page with a custom error message.
@RequestMapping(value = "http/exception4", method = RequestMethod.GET) public String exception4() throws FunctionalException { throw new FunctionalException("Functional exception"); } @ExceptionHandler(FunctionalException.class) public RedirectView handleException4(FunctionalException ex, HttpServletRequest request) throws IOException { RedirectView redirectView = new RedirectView("../errorRedirectPage"); redirectView.addStaticAttribute("errorMessage", ex.getMessage()); return redirectView; } @RequestMapping(value = "errorRedirectPage") public String errorRedirectPage(HttpServletRequest request, Model model, @RequestParam("errorMessage") String errorMessage) { model.addAttribute("errorMessage", errorMessage); return "pages/errorRedirectPage"; }
The above code has some hacks. First in the exception handler we put the error message as static attribute of the redirect view object (line 11). It will end up being appended in the query string:
errorRedirectPage?errorMessage=xxxx
Then in the error message method handler, we extract it from the query with @RequestParam(“errorMessage”) (line 16)and put it back into the model object.
Why such a hack ? Why don’t we use the RedirectAttribute map that exists in Spring MVC 3.1 ? Simply because the ExceptionHandlerExceptionResolver class does not support this method argument.
Of course we could have added the RedirectAttributesMethodArgumentResolver as custom argument resolver but it would require a binderFactory (RedirectAttributesMethodArgumentResolver:53) and the current infrastructure of ExceptionHandlerExceptionResolver does not support.
E Return an exception wrapper object in JSON format, AJAX response
In this strategy, we return an exception wrapper object serialized with JSON format back to the client
@RequestMapping(value = "rest/exception5", method = RequestMethod.GET) public String exception5(HttpSession session) { throw new IllegalArgumentException("Test exception 5 with ExceptionVO as JSON data"); } @ExceptionHandler(IllegalArgumentException.class) @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) @ResponseBody public ExceptionVO handleException5(IllegalArgumentException ex, HttpServletResponse response) throws IOException { ExceptionVO exceptionVO = new ExceptionVO("handleException5()", "HomeController", ex.getMessage()); return exceptionVO; }
To achieve this, we need to add a @ResponseBody annotation to the exception handler method but also a @ResponseStatus annotation to send an Http error code.
Unlike the strategy described at A, in this case we want to trigger the error listener at client-side so we need to send back an Http status other than 200.
function exception5() { $.ajax({ type: 'GET', url: "rest/exception5", dataType: 'application/json; charset=UTF-8', error: function(jqXHR, textStatus, errorThrown) { var exceptionVO = jQuery.parseJSON(jqXHR.responseText); $('#errorModal') .find('.modal-header h3').html(jqXHR.status+' error').end() .find('.modal-body p>strong').html(exceptionVO.clazz).end() .find('.modal-body p>em').html(exceptionVO.method).end() .find('.modal-body p>span').html(exceptionVO.message).end() .modal('show'); } }); return false; }
In the error handler function, we need to extract the exception wrapper object from the body of the returned page (jqXHR.responseText, line 9) and convert it to an JSON object.
Next we can extract various exception information from the wrapper object and display them in a modal panel. The way to display the error details is up to you (modal panel, custom message box …)
III Demo application
The demo application can be found on GitHub at https://github.com/doanduyhai/SpringMVCExceptionGHandling
The main demo page displays a list of buttons to trigger an exception. There is one button for each exception handling strategy described above.
I also added some test cases for the DefaultHandlerExceptionResolver and SimpleMappingExceptionResolver handlers.
Here is an example using a utility
http://www.perficient.com/Solutions-and-Services/Business-Integration-SOA/Reusable-Utility-Services
I currently use an @ExceptionHandler annotated error handling method with a String return value. The method returns a view name which is resolved to a JSP (view) page. Recently, I added a method to this @Controller for handing Ajax requests. When the new method throws an exception, the same handler method is unable to provide any feedback to the browser. What may be further complicating this problem is the fact that the exception is originating from an implementation of HandlerInterceptorAdapter, and not from the Controller method or down that function call stack.
Hello Ashwin
I’ve done a quick test. Even if the exception is thrown in your request interceptor, if you have defined a proper exception handling method at controller level (with the correct Exception class), the exception should be caught.
Looking at the source code of DispatcherServlet.doDispatch() method (DispatcherServlet.doDispatch() ) we can see that all the interceptor handling is wrapped by generic exception catcher (line 942).
Please ensure that you define a generic Exception class for your exception handler and especially put the exception handler in the correct @Controller.
If your interceptor is triggered when calling, let’s say, the /example/test URL, then you should put the exception handler in the @Controller handling this URL.
Hi DuyHai,
I am doing spring mvc+jpa application,i have requirement that reset password option,when the user clicks reset password i am showing current pass,new pass,confirm pass fields ,so that user can reset his password ,if the user enter current password correct every thing works fine,but if user not entered the current password correct it throws 500 exception stack trace that current password is not matching in console,i have to catch that exception ,and i should show that message in the same page ,please help me to solve this exception handling.
Thanks in advance
Hello Venu
You should look at the piece of code that throws the HTTP 500 error. As far as I know the “reset” password feature is not provided out-of-the box by Spring Security so it is done somewhere in your application
Great!Thank you!
Excellent Stuff, Thanks Much!!!!!!!!!
Wt is the difference between (object==null) and (null==object). ?
Which one is better or preferred ?
None, there is no difference. For better readability I would prefer (object == null)
Pingback: fstyle.de » Spring MVC: Exception Handling
This tutorial saved me tons of time, was exactly what I needed.
I’m using Spring MVC 3.2 to build a rich webapp, so there are many ajax requests from the client-side. Your strategy E “Return an exception wrapper object in JSON format, AJAX response” is what I needed.
Thank you so much for taking the time to share your knowledge!
thanks for your great work keep going
Nice tutorial…
I’ve a doubt in throwing exception from DAO layer. If I throw an exception like “throw MyException(“Duplicate entry”)”, I need to show this message in the same screen where user performs submission. Means I do not need to show error message in new screen, but in the same screen at the top. How can achieve this…Pls give some idea to accomplish this…
Pingback: Valider ses POJOs en REST avec Bean validation, Spring MVC et JQuery | Frédéric Camblor Dev Blog
Any examples on handling exceptions thrown from the filter layer and not application, especially popular in the spring security stack
how to get model and view when clicking on back button by using interceptors in spring mvc?
From search page->results page ->detail results page. Each stage there is back button. It should begeneric and useful for all pages.No hardcoded passing values any where.Any suggestion would be appreciate.
I was pretty pleased to uncover this page. I
need to to thank you for ones time for this particularly fantastic read!!
I definitely liked every bit of it and I have you saved as
a favorite to check out new stuff in your site.
Pingback: Fix Labview Application Builder Error 1502 Windows XP, Vista, 7, 8 [Solved]
Pingback: Fix Spring Form Error Handling Windows XP, Vista, 7, 8 [Solved]