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

Spring_FrameworkSpringMVC

Nous discuterons dans cette deuxième partie de la troisième approche: La gestion globale des exceptions dans une application web spring mvc. Ce billet est la suite de la part-1.
La lecture de la première partie peut vous aider à suivre celle-ci.

Dans cette approche globale la gestion technique ne se fait pas au niveau du controlleur individuellement mais centralisée au niveau de toute l'application web.
Cela passe par une classe annotée avec @ControllerAdvice.

Toute classe ainsi annotée devient en quelque sorte un controller-advice et servira à centraliser la gestion des exceptions levées par n'importe quel controlleur!

Est ce une bonne approche dans l'absolu ? Je n'en sais rien, tout dépend du contexte.

Voici une démo de cette approche en quatre parties.

PARTIE 1. Configurer le web.xml

Nous configurons le fichier web.xml en déclarant les pages à afficher. En cas de erreur 404 (document not found) ou erreur http 400 bad request. Nous verrons plus loin l'utilité de ces déclarations.


<error-page>
<error-code>404
<location>/WEB-INF/jsp/error404.jsp
</error-page>
<error-page>
<error-code>400
<location>/WEB-INF/jsp/error400.jsp
</error-page>

PARTIE 2. Classe advice controlleur

Écrivons la classe qui centralise les traitements des exceptions mvc.
Elle sera donc annotée par @ControllerAdvice. Voici un exemple de code :


package fr.netapsys.samples.controllers;

import javax.jcr.PathNotFoundException;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import o.s.http.HttpStatus;
import o.s.web.bind.annotation.ControllerAdvice;
import o.s.web.bind.annotation.ExceptionHandler;
import o.s.web.bind.annotation.ResponseStatus;
import o.s.web.servlet.ModelAndView;
import o.s.web.servlet.config.annotation.EnableWebMvc;
/**Classe centralise les traitements des exceptions mvc!*/
@ControllerAdvice
@EnableWebMvc
public class MyAdviceGlobalHandlerExceptions {

Logger logger=LoggerFactory.getLogger(this.getClass());

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler (PathNotFoundException.class)
public void handlePathNotFound(PathNotFoundException e) {
logger.error(""+e,e);//si besoin!
//nothing
}

@ExceptionHandler (Exception.class)
public ModelAndView defaultHandle(Exception e) throws Exception{
logger.error(""+e,e);
return new ModelAndView ("error","except",e);
}
}

Note : Dans les imports, o.s remplace org.springframework.

Une explication s'impose :

Afin que l'annotation @ControllerAdvice soit bien prise en compte il faudrait ajouter @EnableWebMvc ou bien déclarer dans xml le bloc suivant:

<mvc:annotation-driven />

La classe déclare deux méthodes. Chacune est annotée avec @ExceptionHandler rencontrée durant la 1ère partie. Pour rappel, cette annotation permet d'effectuer le traitement indiqué lorsqu'une exception se produit.

La première méthode


@ResponseStatus(value=HttpStatus.BAD_REQUEST,reason="Bad request!!")
@ExceptionHandler (PathNotFoundException.class)
public void handlePathNotFound

Deux annotations y sont apposées.
L'annotation @ResponseStatus permet de traiter l'exception spécifique PathNotFoundException (en paramètre de l'annotation @ExceptionHandler). Le traitement consiste seulement à tracer les logs puis @responseStatus se charge de retourner le code 400 de la réponse HTTP "Response Status" (400=bad request). Auquel cas, la configuration dans le web.xml concernant l'erreur 400 prend le relais.

La seconde méthode, defaultHandle(Exception e), est annotée uniquement avec @ExceptionHandler.
Ainsi, lorsqu'une autre exception est remontée par un quelconque controlleur, cette méthode la traite. Le traitement ici consiste à tracer les logs puis à remonter la bonne vue via ModelAndView. L'instance de ModelAndVue à retourner définit la vue pointant sur la page error.jsp qui sera donnée ci-après.

PARTIE 3. Classe controlleur mvc

Le controller contient uniquement une seule méthode nommée display:

package fr.netapsys.samples.controllers;

import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import fr.netapsys.samples.services.IService;

@Controller
public class MyController {
@Autowired @Qualifier("service")
IService service;

/** required=false needed otherwise sevice not called and another exception is thrown!**/
@RequestMapping("/display")
public ModelAndView display(
@RequestParam(required=false, defaultValue="root") String nameNode)
throws PathNotFoundException,RepositoryException {
//this call throw exception whene nameNode is empty
String contenu=service.displayNode(nameNode);
return new ModelAndView("display", "contenu",contenu);
}
}

Quelques remarques et rappels:
L'unique méthode du controlleur ne fait aucun try/catch des exceptions. Toute checked exception produite ici est levée.
Comme nous l'avons déjà signalé, la classe controller-advice va intercepter les exceptions levées et effectuer les traitements donnés.

Le controller renvoie aussi la chaîne "display", par conséquent une page display.jsp doit être présente dans /WEB-INF/jsp/.

PARTIE 4. Les pages jsp utiles

Les pages jsp vraiment utiles sont: dispaly.jsp, error.jsp et errorXXX.jsp.
Voici le contenu de la page display.jsp:


<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<body>
<p>Résultat:</p<
<pre>
<c:out value="${contenu}"/<
<p<
</pre<
</body<
</html<

Voici le contenu de la page error.jsp:


<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<p>Erreur (Exception):</p>
<pre>
<c:out value="${msgErr}"/>
<br/>
details:
<c:forEach items="${exception.stackTrace}" var="stac">
${stac}
</c:forEach>
</pre>
</body>
</html>

Vous pouvez dans l'implémentation du service, générer une exception dans la méthode displayNode et ainsi vous obtenez ce genre d'affichage (il sera forcément différent chez vous):


Erreur (Exception):

le chemin nimportkoi est introuvable dans le repo!

details:

org.apache.jackrabbit.core.NodeImpl$8.perform(NodeImpl.java:2167)

org.apache.jackrabbit.core.NodeImpl$8.perform(NodeImpl.java:2161)

org.apache.jackrabbit.core.session.SessionState.perform(SessionState.java:216)

org.apache.jackrabbit.core.ItemImpl.perform(ItemImpl.java:91)

org.apache.jackrabbit.core.NodeImpl.getNode(

 

S'assurer qu'une page jsp nommée error404.jsp existe dans /WEB-INF/jsp/

Signalons quelques difficultés déroutantes rencontrées avec la taglib c de jstl.

Si vous utilisez jstl-1.1.2.jar et la taglib standard-1.1.2.jar, pensez à écrire correctement l'entête d'import dans les pages jsp (notez la présence de /jsp/ devant /jstl/).

@Enjoy

Un commentaire

  1. hello, super tuto,
    J’ai repéré une erreur dans Web.xml, les balises et n’ont pas de balises fermantes.

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.