Spring MVC part V: Exception handling

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.

springmvcexceptionhandling-exception1
 

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>

The result:
springmvcexceptionhandling-exception3

 

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 …)

springmvcexceptionhandling-exception5
 

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.

springmvcexceptionhandling-demo

 
 
 

20 Comments

  1. Ashwin Raj

    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.

    Reply
    1. DuyHai DOAN

      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.

      Reply
  2. venu

    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

    Reply
    1. DuyHai DOAN

      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

      Reply
  3. Fabrizio Giovannetti

    Great!Thank you!

    Reply
  4. Prashant

    Excellent Stuff, Thanks Much!!!!!!!!!

    Reply
  5. Vidya

    Wt is the difference between (object==null) and (null==object). ?
    Which one is better or preferred ?

    Reply
    1. DuyHai DOAN

      None, there is no difference. For better readability I would prefer (object == null)

      Reply
  6. Pingback: fstyle.de » Spring MVC: Exception Handling

  7. Todd P.

    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!

    Reply
  8. Rachid

    thanks for your great work keep going

    Reply
  9. Dsr

    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…

    Reply
  10. Pingback: Valider ses POJOs en REST avec Bean validation, Spring MVC et JQuery | Frédéric Camblor Dev Blog

  11. k

    Any examples on handling exceptions thrown from the filter layer and not application, especially popular in the spring security stack

    Reply
  12. krishna

    how to get model and view when clicking on back button by using interceptors in spring mvc?

    Reply
    1. krishna

      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.

      Reply
  13. groupware technology

    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.

    Reply
  14. Pingback: Fix Labview Application Builder Error 1502 Windows XP, Vista, 7, 8 [Solved]

  15. Pingback: Fix Spring Form Error Handling Windows XP, Vista, 7, 8 [Solved]

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.