This tutorial demonstrates how to handle exceptions in Spring 3 Web MCV.
Emphasis is given to Handling exceptions that occur during AJAX requests. I.e. in the event of an exception during an AJAX request, it is not desirable to redirect to an error page, instead JavaScript error handling code should be invoked with relevant information passed in.
A Netbeans project is available for download at the bottom of this page.
Scope
A few Spring 3 Web MVC Exception handling demonstrations are provided here:
- Delegating exception-detection to the Spring framework but handling at as granular a level as desired through generic code.
- Handling exceptions during AJAX requests, returning specific error messages to the client.
- Handling specific and generic Exceptions at the @Controller level or at the global level.
- Spring 3 Web MVC allows for very comprehensive Exception handling without all the boiler plate try{...} catch{...} code conventionally associated with error handling. I.e. Exception detection is delegated to the Spring framework. Handling the exception (or the specific Exception type) can be done generically.
Overview
Handling All Exceptions at the @Controller level
Methods can be added to any Controller to catch exceptions thrown within that controller.
Annotate an dedicated method within the @Controller with @ExceptionHandler() to have that method invoked when any Exception occurs within that @Controller (org.springframework.web.bind.annotation.ExceptionHandler)
1 2 3 4 5 6 |
@ExceptionHandler() public String iHandleExceptions(HttpServletRequest request, Exception e) { //do loads of interesting stuff to deal with the exception request.setAttribute("exception", e); return "/unchecked"; } |
Handling Specific Exceptions at the @Controller level
1 2 3 4 5 6 |
@ExceptionHandler( value={NullPointerException.class, IllegalAccessException.class} ) public String iHandleSpecificExceptions(HttpServletRequest request, Exception e) { //do loads of interesting stuff to deal with the exception request.setAttribute("exception", e); return "/unchecked"; } |
Note how a comma separated list of Specific exceptions can be specified between the {}’s in the ‘value’ attribute of @ExceptionHandler
In this instance, if either of those two Exception types (or sub-types) are thrown within any of the other @Controller methods, this @ExceptionHandler method is invoked.
Note that this handling is for this Controller only. Please continue reading for information in Global Exception Handling.
Handling All Exceptions or Specific Exceptions at the Global level
A global error handler can be declared in the application context (dispatcher-servlet.xml in the project available for download at the bottom of this page) as so. A popular and comprehensive handler from Spring is: SimpleMappingExceptionResolver but in this example, I’ve extended this class to handle exceptions occurring during AJAX requests in a manor more conducive to ‘the displaying of meaningful popup messages‘.
Declaring an Exception Handler in the Application Context
ExceptionHandler extends SimpleMappingExceptionResolver. It’s three member variables are set in the application context. I.e.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <context:component-scan base-package="com.outbottle" /> <mvc:annotation-driven /> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver" p:order="1" /> <context:property-placeholder location="classpath:messages.properties" /> <bean id="handlerExceptionResolver" class="com.outbottle.support.ExceptionHandler" p:order="2" > <!-- from ExceptionHandler --> <property name="ajaxErrorView" value="ajaxexception" /> <property name="ajaxDefaultErrorMessage" value="${ajaxErrorMessage}" /> <property name="ajaxShowTechMessage" value="false" /> <!-- from SimpleMappingExceptionResolver --> <property name="defaultErrorView" value="checked"/> <property name="exceptionMappings"> <props> <prop key="java.lang.RuntimeException" > unchecked </prop> </props> </property> </bean> |
Things to note here are:
- The exceptionMapping <property> declares that in the event of any RuntimeException (or sub-classes or RuntimeException) the page to be rendered is ‘unchecked.jsp’
- The declaration of the ‘AnnotationMethodHandlerExceptionResolver‘ bean with ‘p:order=”1″‘ means simply that @Controller methods annotated with @ExceptionHandler should be given priority 1 ahead (in this example) of other Exception handlers.
- Bean ‘com.outbottle.support.ExceptionHandler‘ is declared with ‘p:order=”2″‘ giving it lower priority than the other handler. It is also of course the class extending ‘SimpleMappingExceptionResolver‘.
- messages.properties is declared, from which the ${ajaxErrorMessage} is taken. This properties file must be in the classpath.
- The <property> attributes are used to set matching member variables.
p:order
Note that only one handler is executed, they are not chained.
com.outbottle.support.ExceptionHandler
This class extends SimpleMappingExceptionResolver which extends ‘org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver’. The method
1 2 |
@Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) { |
is overridden to provide custom behaviour. This method is invoked when an Exception (not handled in the controller) is thrown.
Handling the Exception Globally
Following on from above, this is how the exceptions are handled…..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
private String ajaxErrorView; private String ajaxDefaultErrorMessage = "An error has occurred"; private boolean ajaxShowTechMessage = true; @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) { if( isAjax(request) ) { String exceptionMessage = ajaxDefaultErrorMessage; if( ajaxShowTechMessage ) exceptionMessage += "\n" + getExceptionMessage(e); ModelAndView m = new ModelAndView(ajaxErrorView); m.addObject("exceptionMessage", exceptionMessage); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return m; } else { return super.resolveException(request, response, o, e); } } private String getExceptionMessage(Throwable e) { String message = ""; while( e != null ) { message += e.getMessage() + "\n"; e = e.getCause(); } return message; } private boolean isAjax(HttpServletRequest request) { return "XMLHttpRequest".equals(request.getHeader("X-Requested-With")); } |
So, in short, set the response.setStatus to some error code thus allow the AJAX error handling code to be invoked. Secondly, return to some view which simply prints out the error messages passed in. The error handling code is as so:
1 2 3 4 5 6 7 8 9 10 11 12 |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script> <script src="http://malsup.github.com/jquery.form.js" type="text/javascript"></script> <script type="text/javascript"> $("#ex").ajaxForm({ success: function() { alert('done successfully'); }, error: function(xhr, status, err) { alert(xhr.responseText); } }); </script> |
Note that in this example this excellent jQuery AJAX form plugin is used.
Conclusion
So that’s it. This tutorial has demonstrated
- How to handle Exceptions at a global level.
- How to handle Exceptions that occur during AJAX requests in a way suitable for displaying popup messages with the client side error handling JavaScript code.
- How to override the global exception handler set in the application-context thus, catching and handling specific exceptions at the @Controller level.
Download
A simple Netbeans project is available for download below which demonstrates everything discussed in this article.
Comments welcome…..