Monday 1 April 2013

Spring 3 MVC Exception Handlers

The controller code below is the first step in generating an error. The idea is that it’s supposed to return the user to our home page, but in the mists of processing the user’s request it throws a simple IOException. Once thrown, the exception is caught by this method:
 /**
   * Whoops, throw an IOException
   */
  @RequestMapping(value = "/ioexception", method = RequestMethod.GET)
  public String throwIoException(Locale locale, Model model) throws IOException {

    logger.info("This will throw an IOExceptiom");

    boolean throwException = true;

    if (throwException) {
      throw new IOException("This is my IOException");
    }

    return "home";
  }
/**
   * Catch IOException and redirect to a 'personal' page
   */
  @ExceptionHandler(IOException.class)
  public ModelAndView handleIOException(IOException ex) {

    logger.info("handleIOException - Catching: " + ex.getClass().getSimpleName());
    return errorModelAndView(ex);
  }

  /**
   * Get the users details for the 'personal' page
   */
  private ModelAndView errorModelAndView(Exception ex) {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("error");
    modelAndView.addObject("name", ex.getClass().getSimpleName());
    modelAndView.addObject("user", userDao.readUserName());

    return modelAndView;
  }
To set this up is really simple, all you need to do is to add:

 @ExceptionHandler(IOException.class)

 to a method signature, et voila you’re done ...and that’s the simple bit over with. There are some points worth noting here: firstly, using

 @ExceptionHandler(IOException.class) 

 …will adhere to the usual contract for exception handling. This means that not only will the method above catch all IOExceptions, it’ll also catch all exceptions that are subclasses of IOException;

hence, if my throwAnException(..) method threw a FileNotFoundException it’ll still be caught by my handleIOException(...) method.

 Secondly, there is a very flexible, but ultimately limited, set of method signatures that you can use for exception handler methods. The full documentation for this is provided by Spring’s JavaDoc, but in summary you can devise a signature that contains any of the following input arguments in any order:

 Exception or one of its subclasses
 ServletRequest or HttpServletRequest
 ServletResponse or HttpServletResponse
 HttpSession
 WebRequest or NativeWebRequest
 Locale
 InputStream or one of its subclasses to access the request’s content
 OutputStream or one of its subclasses to access the response’s content
 Reader or one of its subclasses
 Writer or one of its subclasses

 The method signature must also have one of the following return types:

 ModelAndView
 Model
 Map
 View
 String - interpreted as a view name
 void,  but only if the method writes directly to the response object

 All of which should be enough for any scenario under any circumstance.

 Using @ExceptionHandler gives you the ability to perform fine grained exception handling that targets different error scenarios. In the case of the sample code, I create a new ModelAndView object and populate it with the user’s name in order to personally tell him/her that the system has lost their documents. Some may say that this is a limitation, as @ExceptionHandler is so fine-grained that you can only catch exceptions thrown by the controller that contains your @ExceptionHandler annotated method. I would disagree, if you want to catch exceptions thrown by multiple controllers in one place, then this technique is not for you and you should consider using a SimpleMappingExceptionResolver instead.

 There’s lots to consider when implementing error handling such as: what happens if there’s an error in your error handler? Should you use coarse or fine grained exception handlers? What about setting the HTTP status code? So, my next few blogs will looking into error handling further, demonstrating how to assign multiple exception classes to a single @ExceptionHandler and how to combine the exception handler notation with @ResponseStatus to fine tune your server’s HTTP status code, and may be more...
@Controller
public class ExceptionsDemoController {

private static final Logger logger = LoggerFactory.getLogger(ExceptionsDemoController.class);

@Autowired
private UserDao userDao;

/**
* Whoops, throw an IOException
*/
@RequestMapping(value = "/ioexception", method = RequestMethod.GET)
public String throwAnException(Locale locale, Model model) throws IOException {

logger.info("This will throw an IOException");

boolean throwException = true;

if (throwException) {
throw new IOException("This is my IOException");
}

return "home";
}

/**
* Catch IOException and redirect to a 'personal' page.
*/
@ExceptionHandler(IOException.class)
public ModelAndView handleIOException(IOException ex) {

logger.info("handleIOException - Catching: " + ex.getClass().getSimpleName());
return errorModelAndView(ex);
}

/**
* Get the users details for the 'personal' page
*/
private ModelAndView errorModelAndView(Exception ex) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("name", ex.getClass().getSimpleName());
modelAndView.addObject("user", userDao.readUserName());

return modelAndView;
}

@RequestMapping(value = "/my404", method = RequestMethod.GET)
public String throwNoSuchRequestHandlingMethodException(Locale locale, Model model)
throws NoSuchRequestHandlingMethodException {

logger.info("This will throw a NoSuchRequestHandlingMethodException, which is Spring's 404 not found");

boolean throwException = true;

if (throwException) {
throw new NoSuchRequestHandlingMethodException("This is my NoSuchRequestHandlingMethodException", this.getClass());
}

return "home";
}

@RequestMapping(value = "/nullpointer", method = RequestMethod.GET)
public String throwNullPointerException(Locale locale, Model model) throws NoSuchRequestHandlingMethodException {

logger.info("This will throw a NullPointerException");

String str = null; // Ensure that this is null.
str.length();

return "home";
}

@ExceptionHandler({ NullPointerException.class, NoSuchRequestHandlingMethodException.class })
public ModelAndView handleExceptionArray(Exception ex) {

logger.info("handleExceptionArray - Catching: " + ex.getClass().getSimpleName());
return errorModelAndView(ex);
}

/**
* Throw a DataFormatException
*/
@RequestMapping(value = "/dataformat", method = RequestMethod.GET)
public String throwDataFormatException(Locale locale, Model model) throws DataFormatException {

logger.info("This will throw an DataFormatException");

boolean throwException = true;

if (throwException) {
throw new DataFormatException("This is my DataFormatException");
}

return "home";
}

/**
* If you add/alter in the ResponseStatus - then the server won't cope. Set
* to OK and you get a blank screen. Set to an error (300+) and you'll see
* the web server's default page - so go and fix the server configuration.
*/
@ExceptionHandler(DataFormatException.class)
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "My Response Status Change....!!")
public void handleDataFormatException(DataFormatException ex, HttpServletResponse response) {

logger.info("Handlng DataFormatException - Catching: " + ex.getClass().getSimpleName());
}


0 comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...