Spring Boot Menambahkan Http Request Interceptors

107

Apa cara yang benar untuk menambahkan interseptor HttpRequest di aplikasi boot musim semi? Yang ingin saya lakukan adalah mencatat permintaan dan tanggapan untuk setiap permintaan http.

Dokumentasi boot musim semi tidak mencakup topik ini sama sekali. ( http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ )

Saya menemukan beberapa contoh web tentang cara melakukan hal yang sama dengan versi pegas yang lebih lama, tetapi contoh tersebut berfungsi dengan applicationcontext.xml. Tolong bantu.

riship89.dll
sumber
Hai @ riship89 ... Saya telah HandlerInterceptorberhasil menerapkan . Ini bekerja dengan baik. Hanya masalahnya, beberapa internal HandlerInterceptormelempar pengecualian sebelum ditangani oleh custom HandlerInterceptor. The afterCompletion()Metode yang ditimpa disebut setelah kesalahan dilemparkan oleh pelaksanaan internal HandlerInterceptor. Apakah Anda punya solusi untuk ini?
Chetan Oswal

Jawaban:

155

Karena Anda menggunakan Spring Boot, saya berasumsi Anda lebih suka mengandalkan konfigurasi otomatis Spring jika memungkinkan. Untuk menambahkan konfigurasi khusus tambahan seperti interseptor Anda, cukup berikan konfigurasi atau kacang WebMvcConfigurerAdapter.

Berikut adalah contoh kelas config:

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

CATATAN jangan anotasi ini dengan @EnableWebMvc, jika Anda ingin menyimpan konfigurasi otomatis Spring Boots untuk mvc .

ikumen
sumber
Bagus! Kelihatan bagus! Setiap contoh dari apa yang ada di dalam registry.addInterceptor (...)? Hanya ingin tahu contoh "..."
riship89
6
gunakan anotasi @Component pada yourInjectedInceptor
Paolo Biavati
1
@ riship89 Lihat ini untuk contoh: mkyong.com/spring-mvc/spring-mvc-handler-interceptors-example
Vlad Manuel Mureșan
4
Saya mendapatkan kesalahan The type WebMvcConfigurerAdapter is deprecated. Saya menggunakan Spring Web MVC 5.0.6
John Henckel
7
Di Spring 5, cukup terapkan WebMvcConfigurer alih-alih memperluas WebMvcConfigurerAdapter. Karena antarmuka Java 8 memungkinkan implementasi default, maka tidak perlu lagi menggunakan adaptor (itulah sebabnya tidak digunakan lagi).
edgraaff
88

WebMvcConfigurerAdapterakan dihentikan dengan Spring 5. Dari Javadoc -nya :

@deprecated per 5.0 {@link WebMvcConfigurer} memiliki metode default (dimungkinkan oleh dasar Java 8) dan dapat diterapkan secara langsung tanpa memerlukan adaptor ini

Seperti yang dinyatakan di atas, yang harus Anda lakukan adalah menerapkan WebMvcConfigurerdan mengganti addInterceptorsmetode.

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyCustomInterceptor());
    }
}
sedooe
sumber
10
Jawaban Anda tidak lengkap karena tidak menerapkan penerapanMyCustomInterceptor
peterchaula
33

Untuk menambahkan interseptor ke aplikasi boot musim semi, lakukan hal berikut

  1. Buat kelas interseptor

    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. Tentukan kelas konfigurasi

    @Configuration
    public class MyConfig extends WebMvcConfigurerAdapter{
        @Override
        public void addInterceptors(InterceptorRegistry registry){
            registry.addInterceptor(new MyCustomInterceptor()).addPathPatterns("/**");
        }
    }
  3. Itu dia. Sekarang semua permintaan Anda akan melewati logika yang ditentukan di bawah metode preHandle () dari MyCustomInterceptor.

sunitha
sumber
1
Saya telah mengikuti cara ini untuk mencegat permintaan pendaftaran yang datang ke aplikasi saya untuk melakukan beberapa validasi umum. Tetapi masalahnya adalah saya mendapatkan getReader() has already been called for this requestkesalahan. Apakah ada cara yang lebih sederhana untuk mengatasi ini tanpa menggunakan salinan permintaan sebenarnya?
vigamage
Ketika pre-handler dipanggil, isi permintaan tidak tersedia, tetapi hanya parameter. Untuk melakukan validasi pada isi permintaan, cara yang lebih disukai adalah menggunakan Aspek J dan membuatAdvice
Hima
12

Karena semua tanggapan terhadap hal ini menggunakan Adaptor WebMvcConfigurer abstrak yang sekarang sudah lama tidak digunakan lagi, bukan WebMvcInterface (seperti yang telah disebutkan oleh @sebdooe), berikut ini contoh minimal yang berfungsi untuk aplikasi SpringBoot (2.1.4) dengan 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");
    }
}

bekerja seperti yang diiklankan

Outputnya akan memberi Anda sesuatu seperti:

> 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
Xenonite
sumber
Tetapi ini akan mengharuskan Anda untuk menerapkan semua metode dari WebMvcConfigurer, bukan?
Steven
Tidak, ini adalah antarmuka (Java 8) dengan implementasi default kosong
Tom
9

Saya memiliki masalah yang sama dengan WebMvcConfigurerAdapter yang sudah tidak digunakan lagi. Ketika saya mencari contoh, saya hampir tidak menemukan kode yang diterapkan. Ini adalah bagian dari kode yang berfungsi.

buat kelas yang memperluas 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;
    }

}

lalu Implementasikan antarmuka 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");
    }
}
pengguna2532195
sumber
4
bagaimana u hanya menimpa satu metode pada antarmuka tanpa masalah kompilasi?
xetra11
1
@ xetra11 Saya juga mencoba untuk melihat apakah kita hanya dapat menerapkan satu metode, bukan semua metode lain yang tidak digunakan dalam kasus ini. Apa itu mungkin? Apakah Anda tahu itu?
pengguna09
3
@arjun Yang lain diimplementasikan sebagai metode default berkat Java 8. Alasan ini, dengan mudah, didokumentasikan di kelas deprecated.
Bob
7

Anda juga dapat mempertimbangkan untuk menggunakan pustaka SpringSandwich open source yang memungkinkan Anda secara langsung membuat anotasi di pengontrol Spring Boot yang akan diterapkan interseptor, sama seperti saat Anda memberi anotasi pada rute url Anda.

Dengan begitu, tidak ada String yang rawan kesalahan ketik yang beredar - metode SpringSandwich dan anotasi kelas dengan mudah bertahan dalam pemfaktoran ulang dan memperjelas apa yang diterapkan di mana. (Pengungkapan: Saya adalah penulisnya).

http://springsandwich.com/

Magnus
sumber
Itu terlihat luar biasa! Saya telah membuat masalah yang meminta SpringSandwich tersedia di pusat maven agar lebih mudah digunakan untuk proyek yang sedang dibangun di bawah CI atau yang diterapkan melalui Heroku.
Brendon Dugan
Bagus. Apakah tersedia di repositori pusat maven? Re - blogging springsandwich.com di situs web saya dengan referensi repositori dan referensi git Anda
Arpan Das
1
SpringSandwich sekarang di Maven Central
Magnus
1

Di bawah ini adalah implementasi yang saya gunakan untuk mencegat setiap permintaan HTTP sebelum keluar dan respons yang kembali. Dengan implementasi ini, saya juga memiliki satu titik di mana saya dapat meneruskan nilai header apa pun dengan permintaan tersebut.

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 bawah ini adalah 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
sumber