Spring Boot เพิ่ม Http Request Interceptors


107

วิธีที่ถูกต้องในการเพิ่มตัวสกัดกั้น HttpRequest ในแอปพลิเคชัน Spring Boot คืออะไร? สิ่งที่ฉันต้องการทำคือบันทึกคำขอและการตอบกลับสำหรับคำขอ http ทุกรายการ

เอกสารสปริงบูตไม่ครอบคลุมหัวข้อนี้เลย ( http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ )

ฉันพบตัวอย่างเว็บบางส่วนเกี่ยวกับวิธีการทำเช่นเดียวกันกับ spring เวอร์ชันเก่า แต่ใช้ได้กับ applicationcontext.xml กรุณาช่วย.


สวัสดี @ riship89 ... ฉันดำเนินการHandlerInterceptorสำเร็จแล้ว มันทำงานได้ดี ปัญหาคือบางคนinternal HandlerInterceptorโยนข้อยกเว้นก่อนที่จะจัดการโดยcustom HandlerInterceptor. afterCompletion()วิธีการซึ่งถูกแทนที่เรียกว่าหลังจากข้อผิดพลาดจะโยนโดยการดำเนินการภายในของ HandlerInterceptor คุณมีวิธีแก้ปัญหานี้หรือไม่?
Chetan Oswal

คำตอบ:


156

เนื่องจากคุณใช้ Spring Boot ฉันคิดว่าคุณต้องการพึ่งพาการกำหนดค่าอัตโนมัติของ Spring หากเป็นไปได้ WebMvcConfigurerAdapterหากต้องการเพิ่มการกำหนดค่าที่กำหนดเองเพิ่มเติมเช่นดักของคุณเพียงแค่ให้การกำหนดค่าหรือถั่ว

นี่คือตัวอย่างของคลาส 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() {
        ...
    });
  }
}

หมายเหตุไม่ใส่คำอธิบายประกอบนี้กับ @EnableWebMvc ถ้าคุณต้องการที่จะให้การกำหนดค่าอัตโนมัติฤดูใบไม้ผลิบู๊ทส์สำหรับ MVC


ดี! ดูดี! ตัวอย่างของสิ่งที่อยู่ใน Registry.addInterceptor (... )? อยากรู้อยากเห็นตัวอย่างของ "... "
riship89

6
ใช้คำอธิบายประกอบ @Component บน yourInjectedInceptor
Paolo Biavati

1
@ riship89 ชำระเงินเป็นตัวอย่าง: mkyong.com/spring-mvc/spring-mvc-handler-interceptors-example
Vlad Manuel Mureșan

4
The type WebMvcConfigurerAdapter is deprecatedฉันได้รับข้อผิดพลาด ฉันใช้ Spring Web MVC 5.0.6
John Henckel

7
ใน Spring 5 เพียงแค่ใช้ WebMvcConfigurer แทนการขยาย WebMvcConfigurerAdapter เนื่องจากอินเทอร์เฟซ Java 8 อนุญาตให้ใช้งานดีฟอลต์จึงไม่จำเป็นต้องใช้อะแด็ปเตอร์อีกต่อไป (ซึ่งเป็นสาเหตุที่เลิกใช้งาน)
edgraaff

88

WebMvcConfigurerAdapterจะเลิกใช้กับ Spring 5 จากJavadoc :

@deprecated ตั้งแต่ 5.0 {@link WebMvcConfigurer} มีเมธอดเริ่มต้น (ทำให้เป็นไปได้โดยพื้นฐาน Java 8) และสามารถนำไปใช้โดยตรงโดยไม่ต้องใช้อะแดปเตอร์นี้

ตามที่ระบุไว้ข้างต้นสิ่งที่คุณควรทำคือการใช้WebMvcConfigurerและการลบล้างaddInterceptorsวิธีการ

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

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

10
คำตอบของคุณไม่สมบูรณ์เนื่องจากไม่มีการนำไปใช้MyCustomInterceptor
peterchaula

33

ในการเพิ่ม interceptor ให้กับแอปพลิเคชัน spring boot ให้ทำดังต่อไปนี้

  1. สร้างคลาส interceptor

    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. กำหนดคลาสคอนฟิกูเรชัน

    @Configuration
    public class MyConfig extends WebMvcConfigurerAdapter{
        @Override
        public void addInterceptors(InterceptorRegistry registry){
            registry.addInterceptor(new MyCustomInterceptor()).addPathPatterns("/**");
        }
    }
  3. แค่นั้นแหละ. ตอนนี้คำขอทั้งหมดของคุณจะผ่านตรรกะที่กำหนดไว้ภายใต้เมธอด preHandle () ของ MyCustomInterceptor


1
ฉันได้ทำตามวิธีนี้เพื่อสกัดกั้นคำขอลงทะเบียนที่มาถึงแอปพลิเคชันของฉันเพื่อทำการตรวจสอบความถูกต้องทั่วไป แต่ปัญหาคือฉันได้รับgetReader() has already been called for this requestข้อผิดพลาด มีวิธีใดที่ง่ายกว่านี้ในการแก้ไขปัญหานี้โดยไม่ต้องใช้สำเนาคำขอจริงหรือไม่?
vigamage

เมื่อเรียกตัวจัดการล่วงหน้าเนื้อหาคำขอจะไม่พร้อมใช้งาน แต่มีเพียงพารามิเตอร์เท่านั้นในการตรวจสอบความถูกต้องในเนื้อหาคำขอวิธีที่ต้องการคือใช้ Aspect J และสร้างAdvice
Hima

12

เนื่องจากการตอบสนองทั้งหมดต่อสิ่งนี้ทำให้ใช้อะแดปเตอร์ WebMvcConfigurer นามธรรมที่เลิกใช้งานมานานแล้วแทนที่จะเป็น WebMvcInterface (ตามที่ระบุไว้แล้วโดย @sebdooe) นี่คือตัวอย่างขั้นต่ำที่ใช้งานได้สำหรับแอปพลิเคชัน SpringBoot (2.1.4) กับ Interceptor:

น้อยที่สุด 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");
    }
}

ทำงานได้ตามที่โฆษณา

ผลลัพธ์จะให้สิ่งต่างๆเช่น:

> 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

แต่สิ่งนี้จะทำให้คุณต้องใช้วิธีการทั้งหมดจาก WebMvcConfigurer ใช่ไหม?
Steven

ไม่มีของมัน (Java 8) เชื่อมต่อกับการใช้งานเริ่มต้นที่ว่างเปล่า
ทอม

9

ฉันมีปัญหาเดียวกันกับ WebMvcConfigurerAdapter ที่เลิกใช้งาน เมื่อฉันค้นหาตัวอย่างฉันแทบไม่พบโค้ดที่ใช้งานเลย นี่คือส่วนหนึ่งของรหัสการทำงาน

สร้างคลาสที่ขยาย 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;
    }

}

จากนั้นใช้อินเทอร์เฟซ 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
คุณจะแทนที่วิธีการเดียวบนอินเทอร์เฟซโดยไม่มีปัญหาการคอมไพล์ได้อย่างไร?
xetra11

1
@ xetra11 ฉันพยายามดูว่าเราสามารถใช้วิธีเดียวแทนวิธีอื่น ๆ ทั้งหมดที่ไม่ได้ใช้ในกรณีนี้หรือไม่ เป็นไปได้ไหม? คุณคิดออกหรือไม่?
user09

3
@arjun อื่น ๆ จะถูกนำไปใช้เป็นวิธีการเริ่มต้นด้วย Java 8 เหตุผลนี้สะดวกและบันทึกไว้ในคลาสที่เลิกใช้แล้ว
Bob

7

คุณอาจพิจารณาใช้ไลบรารี SpringSandwich แบบโอเพนซอร์สซึ่งช่วยให้คุณสามารถใส่คำอธิบายประกอบลงในตัวควบคุม Spring Boot ของคุณได้โดยตรงว่าจะใช้ตัวสกัดกั้นใดในลักษณะเดียวกับที่คุณใส่คำอธิบายประกอบเส้นทาง URL ของคุณ

ด้วยวิธีนี้จะไม่มีสตริงที่พิมพ์ผิดลอยอยู่รอบ ๆ - วิธีการของ SpringSandwich และคำอธิบายประกอบคลาสสามารถอยู่รอดได้อย่างง่ายดายในการปรับโครงสร้างใหม่และทำให้ชัดเจนว่ากำลังใช้อะไรอยู่ที่ไหน (การเปิดเผยข้อมูล: ฉันเป็นผู้เขียน)

http://springsandwich.com/


ดูดีมาก! ฉันได้สร้างปัญหาเพื่อขอให้ SpringSandwich พร้อมใช้งานใน maven central เพื่อให้ง่ายต่อการใช้สำหรับโครงการที่สร้างภายใต้ CI หรือที่กำลังปรับใช้ผ่าน Heroku
Brendon Dugan

เยี่ยมมาก มีอยู่ในที่เก็บส่วนกลางของ maven หรือไม่ Re - บล็อกspringandwich.comในเว็บไซต์ของฉันด้วยการอ้างอิงของที่เก็บและการอ้างอิง git ของคุณ
Arpan Das

1
SpringSandwich อยู่ใน Maven Central
Magnus

1

ด้านล่างนี้คือการใช้งานที่ฉันใช้เพื่อสกัดกั้นคำขอ HTTP แต่ละรายการก่อนที่จะออกไปและการตอบสนองที่กลับมา ด้วยการใช้งานนี้ฉันยังมีจุดเดียวที่ฉันสามารถส่งผ่านค่าส่วนหัวใด ๆ กับคำขอได้

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

ด้านล่างนี้คือ 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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.