Introduction à Spring MVC HandlerInterceptor

Introduction à Spring MVC HandlerInterceptor

1. introduction

Dans ce didacticiel, nous allons nous concentrer sur la compréhension des Spring MVCHandlerInterceptoret sur la manière de les utiliser correctement. __

2. Spring MVC Handler

Et pour comprendre l'intercepteur, prenons du recul et regardons lesHandlerMapping. Cela mappe une méthode à une URL, de sorte que lesDispatcherServlet puissent l'invoquer lors du traitement d'une requête.

Et leDispatcherServlet utilise lesHandlerAdapter pour appeler réellement la méthode.

Maintenant que nous comprenons le contexte global -this is where the handler interceptor comes in. Nous utiliserons lesHandlerInterceptor pour effectuer des actions avant le traitement, après le traitement 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 des codes de gestionnaire répétitifs tels que: 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 Maven

Pour utiliserInterceptors, vous devez inclure la section suivante dans une sectiondependencies de votre fichierpom.xml:


    org.springframework
    spring-web
    5.0.6.RELEASE

La dernière version peut être trouvéehere.

4. Intercepteur de manipulateur de ressort

Les intercepteurs travaillant avec lesHandlerMapping sur le framework doivent implémenter l'interfaceHandlerInterceptor.

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éafter le gestionnaire est exécuté

  • afterCompletion() – appeléafter the complete request has finished and view was generated

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

Et une note rapide - la principale différence entreHandlerInterceptor etHandlerInterceptorAdapter est que dans la première, nous devons remplacer les trois méthodes:preHandle(),postHandle() etafterCompletion(), alors que dans le second, 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 depreHandle():

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

Notez que la méthode renvoie une valeurboolean - qui indique à Spring si la demande doit être traitée ultérieurement par un gestionnaire (true) ou non (false).

Ensuite, nous avons une implémentation depostHandle():

@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 requête parHandlerAdapter, mais avant de générer une vue.

Et il peut bien entendu être utilisé de plusieurs 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 deHandlerInterceptor estafterCompletion():

@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 raccordement pour rassembler des statistiques supplémentaires relatives à la demande.

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

5. Intercepteur d'enregistrement personnalisé

Dans cet exemple, nous allons nous concentrer sur la connexion à notre application Web. Tout d'abord, notre classe doit étendreHandlerInterceptorAdapter:

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 des journaux et d'indiquer quelle classe enregistre actuellement des informations sur la sortie spécifiée.

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

5.1. MéthodepreHandle()

Cette méthode est appelée avant de traiter une requête; il retournetrue, pour permettre au framework d'envoyer la requête à la méthode du gestionnaire (ou à l'intercepteur suivant). Si la méthode renvoiefalse, Spring suppose que la demande 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.

Au 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();
}

Enfin, nous visons à obtenir l'adresse IP source de la requête HTTP.

Voici une implémentation simple:

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. MéthodepostHandle()

Ce hook s'exécute lorsqueHandlerAdapter est appelé le gestionnaire mais queDispatcherServlet n'a pas encore rendu la vue.

Nous pouvons utiliser cette méthode pour ajouter des attributs supplémentaires auxModelAndView ou pour déterminer le temps nécessaire à la méthode du gestionnaire pour traiter la demande d’un client.

Dans notre cas, nous enregistrons simplement une requête juste avant queDispatcherServlet ne rende une vue.

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

    log.info("[postHandle][" + request + "]");
}

5.3. MéthodeafterCompletion()

Lorsqu'une demande est terminée et que la vue est rendue, nous pouvons obtenir des données de demande et de réponse, ainsi que des informations sur les exceptions, le cas échéant:

@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

Pour ajouter nos intercepteurs dans la configuration Spring, nous devons remplacer la méthodeaddInterceptors() dans la classeWebConfig qui implémenteWebMvcConfigurer:

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

Nous pouvons obtenir la même configuration en modifiant notre fichier de configuration XML Spring:


    

Avec cette configuration active, l'intercepteur sera actif et toutes les demandes de l'application seront correctement consignées.

Veuillez noter que si plusieurs intercepteurs Spring sont configurés, la méthodepreHandle() est exécutée dans l'ordre de configuration, tandis que les méthodespostHandle() etafterCompletion() sont appelées dans l'ordre inverse.

7. Conclusion

Ce didacticiel est une introduction rapide à l’interception de requêtes HTTP à l’aide de Spring MVC Handler Interceptor.

Tous les exemples et configurations sont disponibles ici surGitHub.