Spring Boot Aggiunta di intercettatori di richieste HTTP


107

Qual è il modo giusto per aggiungere gli intercettori HttpRequest nell'applicazione di avvio primaverile? Quello che voglio fare è registrare le richieste e le risposte per ogni richiesta http.

La documentazione di Spring Boot non copre affatto questo argomento. ( http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ )

Ho trovato alcuni esempi web su come fare lo stesso con le versioni precedenti di Spring, ma funzionano con applicationcontext.xml. Per favore aiuto.


Ciao @ riship89 ... ho implementato HandlerInterceptorcon successo. Funziona bene. Il problema è solo che alcuni internal HandlerInterceptorgenerano un'eccezione prima che venga gestita da custom HandlerInterceptor. Il afterCompletion()metodo che viene sovrascritto viene chiamato dopo che l'errore è stato generato dall'implementazione interna di HandlerInterceptor. Hai una soluzione per questo?
Chetan Oswal

Risposte:


155

Dato che stai usando Spring Boot, presumo che preferiresti fare affidamento sulla configurazione automatica di Spring, ove possibile. Per aggiungere una configurazione personalizzata aggiuntiva come i tuoi interceptor, fornisci semplicemente una configurazione o un bean di WebMvcConfigurerAdapter.

Ecco un esempio di una classe di configurazione:

@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() {
        ...
    });
  }
}

NOTA non annotarlo con @EnableWebMvc, se si desidera mantenere la configurazione automatica di Spring Boots per mvc .


Bello! Sembra buono! Qualche esempio di ciò che entra in registry.addInterceptor (...)? Solo curioso di conoscere un campione di "..."
riship89

6
usa l'annotazione @Component sul tuoInjectedInceptor
Paolo Biavati

1
@ riship89 Dai un'occhiata a questo per un esempio: mkyong.com/spring-mvc/spring-mvc-handler-interceptors-example
Vlad Manuel Mureșan

4
Ottengo errore The type WebMvcConfigurerAdapter is deprecated. Sto usando Spring Web MVC 5.0.6
John Henckel

7
Nella primavera 5, implementa semplicemente WebMvcConfigurer invece di estendere WebMvcConfigurerAdapter. Poiché le interfacce Java 8 consentono implementazioni predefinite, non è più necessario utilizzare l'adattatore (motivo per cui è deprecato).
edgraaff

88

WebMvcConfigurerAdaptersarà deprecato con la primavera 5. Dal suo Javadoc :

@deprecato a partire dalla 5.0 {@link WebMvcConfigurer} ha metodi predefiniti (resi possibili da una baseline Java 8) e può essere implementato direttamente senza la necessità di questo adattatore

Come affermato sopra, quello che dovresti fare è implementare WebMvcConfigurere sovrascrivere il addInterceptorsmetodo.

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

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

10
La tua risposta è incompleta perché manca l'implementazione diMyCustomInterceptor
peterchaula

33

Per aggiungere l'interceptor a un'applicazione di avvio primaverile, eseguire le operazioni seguenti

  1. Crea una classe di intercettatori

    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. Definisci una classe di configurazione

    @Configuration
    public class MyConfig extends WebMvcConfigurerAdapter{
        @Override
        public void addInterceptors(InterceptorRegistry registry){
            registry.addInterceptor(new MyCustomInterceptor()).addPathPatterns("/**");
        }
    }
  3. Questo è tutto. Ora tutte le tue richieste passeranno attraverso la logica definita nel metodo preHandle () di MyCustomInterceptor.


1
Ho seguito questo modo per intercettare le richieste di registrazione pervenute alla mia applicazione per eseguire alcune convalide comuni. Ma il problema è che ricevo l' getReader() has already been called for this requesterrore. Esiste un modo più semplice per superare questo problema senza utilizzare una copia della richiesta effettiva?
vigamage

Quando viene chiamato il pre-gestore, il corpo della richiesta non è disponibile, ma solo i parametri, Per fare la convalida sul corpo della richiesta, il modo preferito sarà quello di utilizzare Aspect J e creareAdvice
Hima

12

Poiché tutte le risposte a questo fanno uso dell'adattatore WebMvcConfigurer astratto ormai deprecato da tempo invece di WebMvcInterface (come già notato da @sebdooe), ecco un esempio minimo funzionante per un'applicazione SpringBoot (2.1.4) con un 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");
    }
}

funziona come pubblicizzato

L'output ti darà qualcosa come:

> 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

Ma questo richiederà l'implementazione di tutti i metodi da WebMvcConfigurer, giusto?
Steven

No, è un'interfaccia (Java 8) con implementazioni predefinite vuote
Tom

9

Ho avuto lo stesso problema di WebMvcConfigurerAdapter deprecato. Quando ho cercato esempi, non ho trovato quasi alcun codice implementato. Ecco un pezzo di codice funzionante.

creare una classe che estenda HandlerInterceptorAdapter

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;
    }

}

quindi implementare l'interfaccia WebMvcConfigurer

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");
    }
}

4
come puoi sovrascrivere un solo metodo su un'interfaccia senza problemi di compilazione?
xetra11

1
@ xetra11 Sto anche cercando di vedere se possiamo implementare solo un metodo invece di tutti gli altri metodi non utilizzati in questo caso. È possibile? L'hai capito?
user09

3
@arjun Gli altri sono implementati come metodi predefiniti grazie a Java 8. Questo ragionamento è, convenientemente, documentato nella classe deprecata.
Bob

7

Potresti anche considerare di utilizzare la libreria SpringSandwich open source che ti consente di annotare direttamente nei tuoi controller Spring Boot quali intercettori applicare, più o meno allo stesso modo in cui annoti le tue rotte URL.

In questo modo, nessuna stringa soggetta a errori di battitura fluttua in giro: il metodo di SpringSandwich e le annotazioni di classe sopravvivono facilmente al refactoring e rendono chiaro cosa viene applicato e dove. (Divulgazione: sono l'autore).

http://springsandwich.com/


Sembra fantastico! Ho creato un problema che richiedeva che SpringSandwich fosse disponibile in Maven Central per renderlo più facile da usare per i progetti che stanno costruendo in CI o che stanno distribuendo tramite Heroku.
Brendon Dugan

Grande. È disponibile nel repository centrale di Maven? Ri-bloggare su springsandwich.com nel mio sito web con il riferimento del tuo repository git e riferimento
Arpan Das

1
SpringSandwich è ora a Maven Central
Magnus

1

Di seguito è riportata un'implementazione che utilizzo per intercettare ogni richiesta HTTP prima che si spenga e la risposta che ritorna. Con questa implementazione, ho anche un unico punto in cui posso passare qualsiasi valore di intestazione con la richiesta.

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===========================================");
}}

Di seguito è riportato il 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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.