Spring Boot Hinzufügen von HTTP-Anforderungs-Interceptors

107

Was ist der richtige Weg, um HttpRequest-Interceptors in der Spring-Boot-Anwendung hinzuzufügen? Ich möchte Anfragen und Antworten für jede http-Anfrage protokollieren.

Die Spring Boot-Dokumentation behandelt dieses Thema überhaupt nicht. ( http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ )

Ich habe einige Webbeispiele gefunden, wie man dasselbe mit älteren Versionen von Spring macht, aber diese funktionieren mit applicationcontext.xml. Bitte helfen Sie.

riship89
quelle
Hi @ riship89 ... Ich habe HandlerInterceptorerfolgreich implementiert . Es funktioniert gut. Das einzige Problem ist, dass einige internal HandlerInterceptoreine Ausnahme auslösen, bevor sie von der behandelt werden custom HandlerInterceptor. Die afterCompletion()überschriebene Methode wird aufgerufen, nachdem der Fehler von der internen Implementierung von HandlerInterceptor ausgelöst wurde. Haben Sie eine Lösung dafür?
Chetan Oswal

Antworten:

155

Da Sie Spring Boot verwenden, sollten Sie sich nach Möglichkeit lieber auf die automatische Konfiguration von Spring verlassen. Um zusätzliche benutzerdefinierte Konfigurationen wie Ihre Interceptors hinzuzufügen, geben Sie einfach eine Konfiguration oder Bean von an WebMvcConfigurerAdapter.

Hier ist ein Beispiel für eine Konfigurationsklasse:

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

  @Autowired 
  HandlerInterceptor yourInjectedInterceptor;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(...)
    ...
    registry.addInterceptor(getYourInterceptor()); 
    registry.addInterceptor(yourInjectedInterceptor);
    // next two should be avoid -- tightly coupled and not very testable
    registry.addInterceptor(new YourInterceptor());
    registry.addInterceptor(new HandlerInterceptor() {
        ...
    });
  }
}

HINWEIS Sie annotieren dies nicht mit @EnableWebMvc, wenn Sie behalten möchten für mvc Frühling Boots - Autokonfiguration .

Ikumen
quelle
Nett! Sieht gut aus! Gibt es ein Beispiel dafür, was in registry.addInterceptor (...) enthalten ist? Nur neugierig, eine Probe von "..."
riship89
6
Verwenden Sie @Component Annotation auf IhremInjectedInceptor
Paolo Biavati
1
@ riship89 Checkout dies für ein Beispiel: mkyong.com/spring-mvc/spring-mvc-handler-interceptors-example
Vlad Manuel Mureșan
4
Ich bekomme einen Fehler The type WebMvcConfigurerAdapter is deprecated. Ich benutze Spring Web MVC 5.0.6
John Henckel
7
Implementieren Sie in Spring 5 einfach WebMvcConfigurer, anstatt WebMvcConfigurerAdapter zu erweitern. Da Java 8-Schnittstellen Standardimplementierungen zulassen, muss der Adapter nicht mehr verwendet werden (weshalb er veraltet ist).
Edgraaff
87

WebMvcConfigurerAdapterwird mit Spring 5 veraltet sein. Aus seinem Javadoc :

@deprecated ab 5.0 {@link WebMvcConfigurer} verfügt über Standardmethoden (ermöglicht durch eine Java 8-Baseline) und kann direkt implementiert werden, ohne dass dieser Adapter erforderlich ist

Wie oben erwähnt, sollten Sie die Methode implementieren WebMvcConfigurerund überschreiben addInterceptors.

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyCustomInterceptor());
    }
}
sedooe
quelle
10
Ihre Antwort ist unvollständig, weil es die Implementierung vonMyCustomInterceptor
Peterchaula
33

Gehen Sie wie folgt vor, um einer Spring Boot-Anwendung einen Interceptor hinzuzufügen

  1. Erstellen Sie eine Interceptor-Klasse

    public class MyCustomInterceptor implements HandlerInterceptor{
    
        //unimplemented methods comes here. Define the following method so that it     
        //will handle the request before it is passed to the controller.
    
        @Override
        public boolean preHandle(HttpServletRequest request,HttpServletResponse  response){
        //your custom logic here.
            return true;
        }
    }
    
  2. Definieren Sie eine Konfigurationsklasse

    @Configuration
    public class MyConfig extends WebMvcConfigurerAdapter{
        @Override
        public void addInterceptors(InterceptorRegistry registry){
            registry.addInterceptor(new MyCustomInterceptor()).addPathPatterns("/**");
        }
    }
    
  3. Das ist es. Jetzt durchlaufen alle Ihre Anforderungen die unter der preHandle () -Methode von MyCustomInterceptor definierte Logik.

Sunitha
quelle
1
Ich bin diesem Weg gefolgt, um die Anmeldeanforderungen abzufangen, die bei meiner Anwendung eingehen, um einige allgemeine Überprüfungen durchzuführen. Aber das Problem ist, ich bekomme den getReader() has already been called for this requestFehler. Gibt es eine einfachere Möglichkeit, dies zu überwinden, ohne eine Kopie der tatsächlichen Anfrage zu verwenden?
Vigamage
Wenn der Pre-Handler aufgerufen wird, ist der Anforderungshauptteil nicht verfügbar, sondern nur Parameter. Um die Validierung des Anforderungshauptteils durchzuführen, wird bevorzugt Aspekt J verwendet und erstelltAdvice
Hima
12

Da alle Antworten darauf den inzwischen längst veralteten abstrakten WebMvcConfigurer-Adapter anstelle des WebMvcInterface (wie bereits von @sebdooe erwähnt) verwenden, ist hier ein funktionierendes Minimalbeispiel für eine SpringBoot (2.1.4) -Anwendung mit einem Interceptor:

Minimal.java:

@SpringBootApplication
public class Minimal
{
    public static void main(String[] args)
    {
        SpringApplication.run(Minimal.class, args);
    }
}

MinimalController.java:

@RestController
@RequestMapping("/")
public class Controller
{
    @GetMapping("/")
    @ResponseBody
    public ResponseEntity<String> getMinimal()
    {
        System.out.println("MINIMAL: GETMINIMAL()");

        return new ResponseEntity<String>("returnstring", HttpStatus.OK);
    }
}

Config.java:

@Configuration
public class Config implements WebMvcConfigurer
{
    //@Autowired
    //MinimalInterceptor minimalInterceptor;

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

MinimalInterceptor.java:

public class MinimalInterceptor extends HandlerInterceptorAdapter
{
    @Override
    public boolean preHandle(HttpServletRequest requestServlet, HttpServletResponse responseServlet, Object handler) throws Exception
    {
        System.out.println("MINIMAL: INTERCEPTOR PREHANDLE CALLED");

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
    {
        System.out.println("MINIMAL: INTERCEPTOR POSTHANDLE CALLED");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception
    {
        System.out.println("MINIMAL: INTERCEPTOR AFTERCOMPLETION CALLED");
    }
}

funktioniert wie angekündigt

Die Ausgabe gibt Ihnen etwas wie:

> Task :Minimal.main()

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.4.RELEASE)

2019-04-29 11:53:47.560  INFO 4593 --- [           main] io.minimal.Minimal                       : Starting Minimal on y with PID 4593 (/x/y/z/spring-minimal/build/classes/java/main started by x in /x/y/z/spring-minimal)
2019-04-29 11:53:47.563  INFO 4593 --- [           main] io.minimal.Minimal                       : No active profile set, falling back to default profiles: default
2019-04-29 11:53:48.745  INFO 4593 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-04-29 11:53:48.780  INFO 4593 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-04-29 11:53:48.781  INFO 4593 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.17]
2019-04-29 11:53:48.892  INFO 4593 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-04-29 11:53:48.893  INFO 4593 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1269 ms
2019-04-29 11:53:49.130  INFO 4593 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-04-29 11:53:49.375  INFO 4593 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-04-29 11:53:49.380  INFO 4593 --- [           main] io.minimal.Minimal                       : Started Minimal in 2.525 seconds (JVM running for 2.9)
2019-04-29 11:54:01.267  INFO 4593 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-04-29 11:54:01.267  INFO 4593 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2019-04-29 11:54:01.286  INFO 4593 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 19 ms
MINIMAL: INTERCEPTOR PREHANDLE CALLED
MINIMAL: GETMINIMAL()
MINIMAL: INTERCEPTOR POSTHANDLE CALLED
MINIMAL: INTERCEPTOR AFTERCOMPLETION CALLED
Xenonit
quelle
Dazu müssen Sie jedoch alle Methoden aus dem WebMvcConfigurer implementieren, oder?
Steven
Nein, es ist eine (Java 8) Schnittstelle mit leeren Standardimplementierungen
Tom
9

Ich hatte das gleiche Problem, dass WebMvcConfigurerAdapter veraltet war. Bei der Suche nach Beispielen habe ich kaum implementierten Code gefunden. Hier ist ein Teil des Arbeitscodes.

Erstellen Sie eine Klasse, die HandlerInterceptorAdapter erweitert

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import me.rajnarayanan.datatest.DataTestApplication;
@Component
public class EmployeeInterceptor extends HandlerInterceptorAdapter {
    private static final Logger logger = LoggerFactory.getLogger(DataTestApplication.class);
    @Override
    public boolean preHandle(HttpServletRequest request, 
            HttpServletResponse response, Object handler) throws Exception {

            String x = request.getMethod();
            logger.info(x + "intercepted");
        return true;
    }

}

Implementieren Sie dann die WebMvcConfigurer-Schnittstelle

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import me.rajnarayanan.datatest.interceptor.EmployeeInterceptor;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    EmployeeInterceptor employeeInterceptor ;

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(employeeInterceptor).addPathPatterns("/employee");
    }
}
user2532195
quelle
4
Wie können Sie nur eine Methode auf einer Schnittstelle ohne Kompilierungsprobleme überschreiben?
Xetra11
1
@ xetra11 Ich versuche auch zu sehen, ob wir nur eine Methode anstelle aller anderen Methoden implementieren können, die in diesem Fall nicht verwendet werden. Ist es möglich? Hast du das herausgefunden?
Arjun
3
@arjun Die anderen werden dank Java 8 als Standardmethoden implementiert . Diese Argumentation ist zweckmäßigerweise in der veralteten Klasse dokumentiert .
Bob
7

Sie können auch die Open-Source-SpringSandwich-Bibliothek verwenden, mit der Sie in Ihren Spring Boot-Controllern direkt angeben können, welche Interceptors angewendet werden sollen, ähnlich wie Sie Ihre URL-Routen mit Anmerkungen versehen.

Auf diese Weise schweben keine zu Tippfehlern neigenden Strings herum - SpringSandwichs Methoden- und Klassenanmerkungen überleben das Refactoring problemlos und machen deutlich, was wo angewendet wird. (Offenlegung: Ich bin der Autor).

http://springsandwich.com/

Magnus
quelle
Das sieht super aus! Ich habe ein Problem erstellt, bei dem SpringSandwich in Maven Central verfügbar sein soll, um die Verwendung für Projekte zu vereinfachen, die unter CI erstellt oder über Heroku bereitgestellt werden.
Brendon Dugan
Toll. Ist es im zentralen Maven-Repository verfügbar? Erneutes Bloggen von springsandwich.com auf meiner Website mit der Referenz Ihres Git-Repositorys und der Referenz
Arpan Das
1
SpringSandwich ist jetzt in Maven Central
Magnus
1

Im Folgenden finden Sie eine Implementierung, mit der ich jede HTTP-Anforderung abfange, bevor sie ausgeht, und die Antwort, die zurückkommt. Mit dieser Implementierung habe ich auch einen einzelnen Punkt, an dem ich einen beliebigen Header-Wert mit der Anforderung übergeben kann.

public class HttpInterceptor implements ClientHttpRequestInterceptor {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public ClientHttpResponse intercept(
        HttpRequest request, byte[] body,
        ClientHttpRequestExecution execution
) throws IOException {
    HttpHeaders headers = request.getHeaders();
    headers.add("Accept", MediaType.APPLICATION_JSON_UTF8_VALUE);
    headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
    traceRequest(request, body);
    ClientHttpResponse response = execution.execute(request, body);
    traceResponse(response);
    return response;
}

private void traceRequest(HttpRequest request, byte[] body) throws IOException {
    logger.info("===========================Request begin======================================");
    logger.info("URI         : {}", request.getURI());
    logger.info("Method      : {}", request.getMethod());
    logger.info("Headers     : {}", request.getHeaders() );
    logger.info("Request body: {}", new String(body, StandardCharsets.UTF_8));
    logger.info("==========================Request end=========================================");
}

private void traceResponse(ClientHttpResponse response) throws IOException {
    logger.info("============================Response begin====================================");
    logger.info("Status code  : {}", response.getStatusCode());
    logger.info("Status text  : {}", response.getStatusText());
    logger.info("Headers      : {}", response.getHeaders());
    logger.info("=======================Response end===========================================");
}}

Unten ist die Rest Template Bean

@Bean
public RestTemplate restTemplate(HttpClient httpClient)
{
    HttpComponentsClientHttpRequestFactory requestFactory =
            new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);
    RestTemplate restTemplate=  new RestTemplate(requestFactory);
    List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
    if (CollectionUtils.isEmpty(interceptors))
    {
        interceptors = new ArrayList<>();
    }
    interceptors.add(new HttpInterceptor());
    restTemplate.setInterceptors(interceptors);

    return restTemplate;
}
Sylvester
quelle