Introduction à Spring MVC HandlerInterceptor

1. Introduction

Dans ce didacticiel, nous allons nous concentrer sur la compréhension du Spring MVC HandlerInterceptor et sur la manière de l'utiliser correctement.

2. Spring MVC Handler

Afin de comprendre l'intercepteur, prenons un peu de recul et regardons le HandlerMapping . Cela mappe une méthode à une URL, afin que DispatcherServlet puisse l'invoquer lors du traitement d'une demande.

Et le DispatcherServlet utilise le HandlerAdapter pour appeler réellement la méthode.

Maintenant que nous comprenons le contexte général, c'est là que l'intercepteur de gestionnaire entre en jeu . Nous utiliserons HandlerInterceptor pour effectuer des actions avant la gestion, après la gestion ou après l'achèvement (lorsque la vue est rendue), d'une requête.

L'intercepteur peut être utilisé pour des problèmes transversaux et pour éviter le code de gestionnaire répétitif comme la journalisation, la modification des paramètres utilisés globalement dans le modèle Spring, etc.

Dans les prochaines sections, c'est exactement ce que nous allons examiner - les différences entre les différentes implémentations d'intercepteurs.

3. Dépendances de Maven

Pour utiliser les intercepteurs , vous devez inclure la section suivante dans une section des dépendances de votre fichier pom.xml :

 org.springframework spring-web 5.2.8.RELEASE 

La dernière version peut être trouvée ici.

4. Intercepteur Spring Handler

Les intercepteurs travaillant avec HandlerMapping sur le framework doivent implémenter l' interface HandlerInterceptor .

Cette interface contient trois méthodes principales:

  • prehandle () - appelé avant l'exécution du gestionnaire réel, mais la vue n'est pas encore générée
  • postHandle () - appelé après l'exécution du gestionnaire
  • afterCompletion () - appelé une fois la requête complète terminée et la vue générée

Ces trois méthodes offrent la flexibilité nécessaire pour effectuer toutes sortes de pré et post-traitement.

Et une note rapide - la principale différence entre HandlerInterceptor et HandlerInterceptorAdapter est que dans la première, nous devons remplacer les trois méthodes: preHandle () , postHandle () et afterCompletion () , alors que dans la seconde, nous pouvons implémenter uniquement les méthodes requises.

Une petite note avant d'aller plus loin - si vous voulez sauter la théorie et passer directement aux exemples, passez directement à la section 5.

Voici à quoi ressemblera une implémentation simple de preHandle () :

@Override public boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // your code return true; }

Notez que la méthode renvoie une valeur booléenne - qui indique à Spring si la demande doit être traitée ultérieurement par un gestionnaire ( vrai ) ou non ( faux ).

Ensuite, nous avons une implémentation de postHandle () :

@Override public void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // your code }

Cette méthode est appelée immédiatement après le traitement de la demande par HandlerAdapter , mais avant de générer une vue.

Et il peut bien sûr être utilisé de nombreuses manières - par exemple, nous pouvons ajouter un avatar d'un utilisateur connecté dans un modèle.

La dernière méthode que nous devons implémenter dans l' implémentation personnalisée de HandlerInterceptor est afterCompletion ():

@Override public void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // your code }

Lorsque la vue est générée avec succès, nous pouvons utiliser ce hook pour faire des choses comme recueillir des statistiques supplémentaires liées à la demande.

Une dernière remarque à retenir est qu'un HandlerInterceptor est enregistré dans le bean DefaultAnnotationHandlerMapping , qui est responsable de l'application des intercepteurs à toute classe marquée d'une annotation @Controller . De plus, vous pouvez spécifier n'importe quel nombre d'intercepteurs dans votre application Web.

5. Intercepteur d'enregistrement personnalisé

Dans cet exemple, nous nous concentrerons sur la connexion dans notre application Web. Tout d'abord, notre classe doit étendre HandlerInterceptorAdapter :

public class LoggerInterceptor extends HandlerInterceptorAdapter { ... }

Nous devons également activer la journalisation dans notre intercepteur:

private static Logger log = LoggerFactory.getLogger(LoggerInterceptor.class);

Cela permet à Log4J d'afficher les journaux, ainsi que d'indiquer quelle classe enregistre actuellement les informations dans la sortie spécifiée.

Ensuite, concentrons-nous sur les implémentations d'intercepteurs personnalisés:

5.1. Méthode preHandle ()

Cette méthode est appelée avant de traiter une requête; il retourne true, pour permettre au framework d'envoyer la requête plus loin à la méthode du gestionnaire (ou à l'intercepteur suivant). Si la méthode retourne false , Spring suppose que la requête a été traitée et qu'aucun autre traitement n'est nécessaire.

Nous pouvons utiliser le hook pour enregistrer des informations sur les paramètres des requêtes: d'où provient la requête, etc.

Dans notre exemple, nous enregistrons ces informations à l'aide d'un simple enregistreur Log4J:

@Override public boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("[preHandle][" + request + "]" + "[" + request.getMethod() + "]" + request.getRequestURI() + getParameters(request)); return true; } 

Comme nous pouvons le voir, nous enregistrons des informations de base sur la demande.

Dans le cas où nous rencontrions un mot de passe ici, nous devrons nous assurer de ne pas l'enregistrer bien sûr.

Une option simple consiste à remplacer les mots de passe et tout autre type de données sensibles par des étoiles.

Voici une mise en œuvre rapide de la façon dont cela peut être fait:

private String getParameters(HttpServletRequest request) { StringBuffer posted = new StringBuffer(); Enumeration e = request.getParameterNames(); if (e != null) { posted.append("?"); } while (e.hasMoreElements()) { if (posted.length() > 1) { posted.append("&"); } String curr = (String) e.nextElement(); posted.append(curr + "="); if (curr.contains("password") || curr.contains("pass") || curr.contains("pwd")) { posted.append("*****"); } else { posted.append(request.getParameter(curr)); } } String ip = request.getHeader("X-FORWARDED-FOR"); String ipAddr = (ip == null) ? getRemoteAddr(request) : ip; if (ipAddr!=null && !ipAddr.equals("")) { posted.append("&_psip=" + ipAddr); } return posted.toString(); }

Finally, we're aiming to get the source IP address of the HTTP request.

Here's a simple implementation:

private String getRemoteAddr(HttpServletRequest request) { String ipFromHeader = request.getHeader("X-FORWARDED-FOR"); if (ipFromHeader != null && ipFromHeader.length() > 0) { log.debug("ip from proxy - X-FORWARDED-FOR : " + ipFromHeader); return ipFromHeader; } return request.getRemoteAddr(); }

5.2. Method postHandle()

This hook runs when the HandlerAdapter is invoked the handler but DispatcherServlet is yet to render the view.

We can use this method to add additional attributes to the ModelAndView or to determine the time taken by handler method to process a client's request.

In our case, we simply log a request just before DispatcherServlet is going to render a view.

@Override public void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("[postHandle][" + request + "]"); }

5.3. Method afterCompletion()

When a request is finished and the view is rendered, we may obtain request and response data, as well as information about exceptions, if any occurred:

@Override public void afterCompletion( HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception { if (ex != null){ ex.printStackTrace(); } log.info("[afterCompletion][" + request + "][exception: " + ex + "]"); }

6. Configuration

To add our interceptors into Spring configuration, we need to override addInterceptors() method inside WebConfig class that implements WebMvcConfigurer:

@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoggerInterceptor()); }

We may achieve the same configuration by editing our XML Spring configuration file:

With this configuration active, the interceptor will be active and all requests in the application will be properly logged.

Please notice, if multiple Spring interceptors are configured, the preHandle() method is executed in the order of configuration, whereas postHandle() and afterCompletion() methods are invoked in the reverse order.

If we're using Spring Boot instead of vanilla Spring, we should keep in mind to not annotate our configuration class with @EnableWebMvc, or we'll lose out on Boot's auto configurations.

7. Conclusion

This tutorial is a quick introduction to intercepting HTTP requests using Spring MVC Handler Interceptor.

All examples and configurations are available here on GitHub.