Einführung in Spring MVC HandlerInterceptor

1. Einleitung

In diesem Tutorial konzentrieren wir uns auf das Verständnis des Spring MVC HandlerInterceptor und dessen korrekte Verwendung.

2. Feder-MVC-Handler

Um den Interceptor zu verstehen, treten wir einen Schritt zurück und schauen uns das HandlerMapping an . Dadurch wird eine Methode einer URL zugeordnet, sodass das DispatcherServlet sie bei der Verarbeitung einer Anforderung aufrufen kann.

Und das DispatcherServlet verwendet den HandlerAdapter , um die Methode tatsächlich aufzurufen.

Nachdem wir den Gesamtkontext verstanden haben, kommt hier der Handler-Interceptor ins Spiel . Wir werden den HandlerInterceptor verwenden , um Aktionen vor der Bearbeitung, nach der Behandlung oder nach Abschluss (wenn die Ansicht gerendert wird) einer Anforderung auszuführen.

Der Interceptor kann für Querschnittsthemen und zur Vermeidung sich wiederholender Handler-Codes wie Protokollierung, Änderung global verwendeter Parameter im Spring-Modell usw. verwendet werden.

In den nächsten Abschnitten werden wir uns genau damit befassen - die Unterschiede zwischen verschiedenen Interceptor-Implementierungen.

3. Maven-Abhängigkeiten

Um Interceptors verwenden zu können , müssen Sie den folgenden Abschnitt in einen Abhängigkeitsabschnitt Ihrer pom.xml- Datei aufnehmen:

 org.springframework spring-web 5.2.8.RELEASE 

Die neueste Version finden Sie hier.

4. Spring Handler Interceptor

Interceptors, die mit dem HandlerMapping im Framework arbeiten, müssen die HandlerInterceptor- Schnittstelle implementieren .

Diese Schnittstelle enthält drei Hauptmethoden:

  • prehandle () - wird aufgerufen, bevor der eigentliche Handler ausgeführt wird, aber die Ansicht wird noch nicht generiert
  • postHandle () - wird aufgerufen, nachdem der Handler ausgeführt wurde
  • afterCompletion () - wird aufgerufen, nachdem die vollständige Anforderung abgeschlossen und die Ansicht generiert wurde

Diese drei Methoden bieten Flexibilität für alle Arten der Vor- und Nachbearbeitung.

Und eine kurze Anmerkung - der Hauptunterschied zwischen HandlerInterceptor und HandlerInterceptorAdapter besteht darin, dass wir in der ersten alle drei Methoden überschreiben müssen: preHandle () , postHandle () und afterCompletion () , während wir in der zweiten möglicherweise nur die erforderlichen Methoden implementieren.

Eine kurze Anmerkung, bevor wir weiter gehen - wenn Sie die Theorie überspringen und direkt zu Beispielen springen möchten, springen Sie direkt in Abschnitt 5.

So sieht eine einfache preHandle () - Implementierung aus:

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

Beachten Sie, dass die Methode einen booleschen Wert zurückgibt, der Spring mitteilt, ob die Anforderung von einem Handler weiterverarbeitet werden soll ( true ) oder nicht ( false ).

Als nächstes haben wir eine Implementierung von postHandle () :

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

Diese Methode wird unmittelbar nach der Verarbeitung der Anforderung durch HandlerAdapter , jedoch vor dem Generieren einer Ansicht aufgerufen .

Und es kann natürlich auf viele Arten verwendet werden - zum Beispiel können wir einem Modell einen Avatar eines angemeldeten Benutzers hinzufügen.

Die letzte Methode, die wir in der benutzerdefinierten HandlerInterceptor- Implementierung implementieren müssen, ist afterCompletion ():

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

Wenn die Ansicht erfolgreich generiert wurde, können wir diesen Hook verwenden, um beispielsweise zusätzliche Statistiken zur Anforderung zu sammeln.

Ein letzter Hinweis ist, dass ein HandlerInterceptor in der DefaultAnnotationHandlerMapping- Bean registriert ist, die für das Anwenden von Interceptors auf jede Klasse verantwortlich ist, die mit einer @ Controller- Annotation gekennzeichnet ist. Darüber hinaus können Sie in Ihrer Webanwendung eine beliebige Anzahl von Interceptors angeben.

5. Benutzerdefinierter Logger Interceptor

In diesem Beispiel konzentrieren wir uns auf die Anmeldung in unserer Webanwendung. Zunächst muss unsere Klasse HandlerInterceptorAdapter erweitern :

public class LoggerInterceptor extends HandlerInterceptorAdapter { ... }

Wir müssen auch die Protokollierung in unserem Interceptor aktivieren:

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

Auf diese Weise kann Log4J Protokolle anzeigen und angeben, welche Klasse derzeit Informationen für die angegebene Ausgabe protokolliert.

Als nächstes konzentrieren wir uns auf benutzerdefinierte Interceptor-Implementierungen:

5.1. Methode preHandle ()

Diese Methode wird aufgerufen, bevor eine Anforderung verarbeitet wird. Es gibt true zurück, damit das Framework die Anforderung weiter an die Handler-Methode (oder an den nächsten Interceptor) senden kann. Wenn die Methode false zurückgibt , geht Spring davon aus, dass die Anforderung verarbeitet wurde und keine weitere Verarbeitung erforderlich ist.

Wir können den Hook verwenden, um Informationen über die Parameter der Anforderungen zu protokollieren: woher die Anforderung kommt usw.

In unserem Beispiel protokollieren wir diese Informationen mit einem einfachen Log4J-Logger:

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

Wie wir sehen können, protokollieren wir einige grundlegende Informationen über die Anfrage.

Falls wir hier auf ein Passwort stoßen, müssen wir natürlich sicherstellen, dass wir das nicht protokollieren.

Eine einfache Möglichkeit besteht darin, Kennwörter und andere vertrauliche Datentypen durch Sterne zu ersetzen.

Hier ist eine schnelle Implementierung, wie dies getan werden kann:

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.