Gestion globale des exceptions dans spring mvc (global exception handling in spring mvc)-Part 1

Spring_FrameworkSpringMVC

L'objet de ce billet est double :

- Meilleure gestion des exceptions/erreurs dans une application web mvc en affichant une page appropriée et un message explicite,
- Privilégier une gestion globale et transverse à toute l'appli web (tous les controlleurs) de spring mvc.

Ce dernier point nécessite d'être précisé, c'est ce que nous allons faire ci-après.

Rappelons avant cela que plusieurs approches d'interception (handle) des exceptions existent dans spring mvc. Peut-être cette multitude d'approches est source de confusion pour beaucoup de développeurs !

Rappelons aussi que normalement toute exception non gérée au moment de traiter une requête web engendre une erreur 500 (HTTP RESPONSE 500). On ne parle pas de 404 ici.

Et l'utilisateur de l'application web se voit éventuellement afficher une page avec plein de messages d'erreurs (voir plus loin une capture). Et ce n'est pas beau à voir.

Revenons à notre point, spring mvc offre trois approches d'interception (handle) des exceptions :
- Gestion par exception,
- Gestion par controlleur,
- Gestion globalisée à toute l'application.

C'est donc la troisième approche que nous allons particulièrement illustrer dans ce billet. Néanmoins un détour rapide sur la deuxième approche va être fait dans cette partie.

NOTES

  • La gestion globale (au sens large) des exceptions dans une webapp ne se fait pas uniquement dans la servlet dispatcher. Dans le web.xml, on peut gérer des Urls erronés..etc.
  • La DispatcherServlet est déjà configuré pour gérer quelques exceptions....

Commençons par un premier exemple de gestion des exceptions au niveau du controller (seconde approche). Spring 4 et Java 6 sont utilisés pour la démo.

L'avantage évident de cette approche est que toutes les méthodes du controller n'ont pas à se soucier des exceptions. Donc pas de try/catch dans ces méthodes car la gestion va être centralisée au niveau d'une seule méthode annotée convenablement.

Supposons que notre interface IService de la couche service possède une seule méthode ayant cette signature:


public IService{
String displayNode(String nameNode) throws PathNotFoundException, RepositoryException;
}

Les détails d'implémentation ne sont pas utiles pour la suite. Pour la démo, vous pouvez compléter l'implémentation en 'throwant' juste une des deux exceptions.

Écrivons notre controller spring mvc qui va appeler la méthode displayNode du service.


public class MyController {

@Autowired
IService service;

@RequestMapping("/displayNode")
public ModelAndView display(String nameNode) throws PathNotFoundException,RepositoryException {
String contenu=service.displayNode(nameNode);

return new ModelAndView("affiche", "contenu",contenu);
}
}

Noter la signature de la méthode


public ModelAndView display ... throws PathNotFoundException,RepositoryException

qui lève les deux exceptions issues du service.

Testons ce controller via l'url:
http://localhost:8080/displayNode.html?nameNode=nimportekoi

En cas d'exception remontée, vous allez avoir ce genre de lignes affichées (paBo!):


Etat HTTP 500 - Request processing failed; nested exception is
type Rapport d'exception message Request processing failed; nested exception is javax.jcr.PathNotFoundException: xxxx
description Le serveur a rencontré une erreur interne qui l'a empêché de satisfaire la requête.
exception
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.jcr.PathNotFoundException
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:723)
cause mère
javax.jcr.PathNotFoundException: xxxx
org.apache.jackrabbit.core
.....

 

Nous allons enrichir le code de notre controller spring mvc pour ajouter une gestion (déjà fine) des exceptions qui peuvent se produire.
La signature de la méthode du controlleur lève les deux checked exceptions PathNotFoundException et RepositoryException.
C'est pour cela que nous ajoutons une méthode annotée avec @ExceptionHandler prenant ces deux exceptions en paramètre et renvoie en retour un ModelAndView dont la vue ici est "error.jsp" et le model stocke l'exception générée.


public class MyController {
@Autowired
IService service;
private static Logger logger = LoggerFactory.getLogger("MyController");

@ExceptionHandler(
{PathNotFoundException.class,RepositoryException.class})
public ModelAndView handleEx(Exception e, HttpServletRequest req) {
final String errMsg="Url="+req.getRequestURI()+" a provoqué cette erreur:"+e.getMessage();
logger.error(e.getMessage(),e);
return new ModelAndView("error","exception", "At "+Calendar.getInstance().getTime()+ "-> "+ ClassUtils.getShortName(e.getClass()) + ": "+errMsg);
}

// cette partie est inchangée
@RequestMapping("/displayNode")
public ModelAndView display( String nameNode) throws PathNotFoundException,RepositoryException {
String contenu=service.displayNode(nameNode);

return new ModelAndView("affiche", "contenu",contenu);
}
}

Expliquons un peu :

Au niveau du controlleur de spring mvc, l'annotation @ExceptionHandler apposée sur la méthode handleEx permettra d'intercepter (de manière centralisée par controller) toutes les exceptions en paramètre remontées par chacune des méthodes.
Ainsi, comme vous le constatez aucune de ces exceptions ne sont traitées par un try/catch et elles sont throwées, à juste titre, comme la signature de la méthode "display" du controlleur le confirme.
Pour la démo, on aura besoin d'une page error.jsp sous /WEB-INF/jsp (par exemple). Le code de cette page est simple:


<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<html>
<body>
<p>Erreur (Exception):</p>
<pre>
<c:out value="${exception}"/>
</pre>
</body>
</html>

Avec cette configuration, saisissez l'url précédente vous allez désormais avoir ce genre d'affichage lorsqu'une exception se produit:


Erreur (Exception):

At Wed Sep 17 11:12:16 CEST 2014-> PathNotFoundException: url=../displayNode.html a provoqué cette erreur:nimportekoi

A vous de personnaliser (et la date aussi!) le message d'erreur (please, en français).
Et dans les logs de l'application vous aurez la stacktrace.

Dans la partie 2, nous passons à la troisième approche permettant donc de centraliser davantage la gestion des exceptions.

@Bientôt,

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.