Spring Boot Thêm bộ chặn yêu cầu Http


107

Cách phù hợp để thêm bộ chặn HttpRequest trong ứng dụng khởi động mùa xuân là gì? Những gì tôi muốn làm là ghi lại các yêu cầu và phản hồi cho mọi yêu cầu http.

Tài liệu khởi động mùa xuân hoàn toàn không đề cập đến chủ đề này. ( http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ )

Tôi đã tìm thấy một số mẫu web về cách thực hiện tương tự với các phiên bản cũ hơn của spring, nhưng những mẫu đó hoạt động với applicationcontext.xml. Xin vui lòng giúp đỡ.


Chào @ riship89 ... Tôi đã thực hiện HandlerInterceptorthành công. Nó đang hoạt động tốt. Chỉ có vấn đề là, một số internal HandlerInterceptorném ra một ngoại lệ trước khi nó được xử lý bởi custom HandlerInterceptor. Các afterCompletion()phương pháp đó là ghi đè được gọi sau khi lỗi được ném bởi việc thực hiện nội bộ của HandlerInterceptor. Bạn có giải pháp cho điều này?
Chetan Oswal

Câu trả lời:


155

Vì bạn đang sử dụng Spring Boot, tôi cho rằng bạn muốn dựa vào cấu hình tự động của Spring nếu có thể. Để thêm cấu hình tùy chỉnh bổ sung như bộ đánh chặn của bạn, chỉ cần cung cấp cấu hình hoặc bean của WebMvcConfigurerAdapter.

Đây là một ví dụ về lớp cấu hình:

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

LƯU Ý không chú thích điều này với @EnableWebMvc, nếu bạn muốn giữ cấu hình tự động Spring Boots cho mvc .


Đẹp! Có vẻ tốt! Bất kỳ ví dụ nào về những gì bên trong registry.addInterceptor (...)? Chỉ tò mò muốn biết một mẫu "..."
riship89

6
sử dụng chú thích @Component trên yourInjectedInceptor
Paolo Biavati 22/02/16


4
Tôi nhận lỗi The type WebMvcConfigurerAdapter is deprecated. Tôi đang sử dụng Spring Web MVC 5.0.6
John Henckel

7
Trong Spring 5, chỉ cần triển khai WebMvcConfigurer thay vì mở rộng WebMvcConfigurerAdapter. Vì các giao diện Java 8 cho phép triển khai mặc định nên không còn bắt buộc phải sử dụng bộ điều hợp (đó là lý do tại sao nó không được dùng nữa).
edgraaff

88

WebMvcConfigurerAdaptersẽ không được dùng nữa với Spring 5. Từ Javadoc của nó :

@deprecated kể từ 5.0 {@link WebMvcConfigurer} có các phương thức mặc định (có thể thực hiện theo đường cơ sở Java 8) và có thể được triển khai trực tiếp mà không cần bộ điều hợp này

Như đã nêu ở trên, những gì bạn nên làm là thực hiện WebMvcConfigurervà ghi đè addInterceptorsphương thức.

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

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

10
Câu trả lời của bạn là không đầy đủ vì nó còn thiếu việc thực hiệnMyCustomInterceptor
peterchaula

33

Để thêm bộ chặn vào ứng dụng khởi động mùa xuân, hãy làm như sau

  1. Tạo một lớp đánh chặn

    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. Xác định một lớp cấu hình

    @Configuration
    public class MyConfig extends WebMvcConfigurerAdapter{
        @Override
        public void addInterceptors(InterceptorRegistry registry){
            registry.addInterceptor(new MyCustomInterceptor()).addPathPatterns("/**");
        }
    }
  3. Đó là nó. Bây giờ tất cả các yêu cầu của bạn sẽ chuyển qua logic được xác định trong phương thức preHandle () của MyCustomInterceptor.


1
Tôi đã làm theo cách này để chặn các yêu cầu đăng ký đến ứng dụng của tôi để thực hiện một số xác thực thông thường. Nhưng vấn đề là tôi nhận được getReader() has already been called for this requestlỗi. Có cách nào đơn giản hơn để vượt qua điều này mà không cần sử dụng bản sao của yêu cầu thực tế không?
cảnh giác

Khi tiền xử lý được gọi là, cơ thể yêu cầu không có sẵn, nhưng chỉ có các thông số, Để làm được xác nhận trên cơ thể theo yêu cầu, cách ưa thích sẽ được sử dụng Aspect J và tạoAdvice
Hima

12

Vì tất cả các phản hồi cho điều này đều sử dụng Bộ điều hợp WebMvcConfigurer trừu tượng đã lâu không được dùng nữa thay vì Giao diện WebMvcInterface (như đã được @sebdooe lưu ý), đây là một ví dụ tối thiểu hoạt động cho ứng dụng SpringBoot (2.1.4) với Bộ chặn:

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

hoạt động như quảng cáo

Đầu ra sẽ cung cấp cho bạn một cái gì đó như:

> 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

Nhưng điều này sẽ yêu cầu bạn triển khai tất cả các phương thức từ WebMvcConfigurer, phải không?
Steven

Không, giao diện của nó là (Java 8) với các triển khai mặc định trống
Tom

9

Tôi gặp vấn đề tương tự về việc WebMvcConfigurerAdapter không được dùng nữa. Khi tôi tìm kiếm các ví dụ, tôi hầu như không tìm thấy bất kỳ mã được triển khai nào. Đây là một đoạn mã làm việc.

tạo một lớp mở rộng 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;
    }

}

sau đó Triển khai giao diện 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
làm thế nào bạn có thể chỉ ghi đè một phương thức trên một giao diện mà không có vấn đề biên dịch?
xetra11

1
@ xetra11 Tôi cũng đang thử xem liệu chúng ta chỉ có thể triển khai một phương thức thay vì tất cả các phương thức khác không được sử dụng trong trường hợp này hay không. Nó có khả thi không? Bạn đã tìm ra điều đó?
09

3
@arjun Các phương thức khác được triển khai dưới dạng các phương thức mặc định nhờ Java 8. Lý luận này, thuận tiện, được ghi lại trong lớp không dùng nữa.
Bob

7

Bạn cũng có thể cân nhắc sử dụng thư viện SpringSandwich mã nguồn mở cho phép bạn chú thích trực tiếp trong bộ điều khiển Spring Boot của mình những bộ chặn nào sẽ áp dụng, giống như cách bạn chú thích các tuyến url của mình.

Bằng cách đó, không có Chuỗi dễ mắc lỗi đánh máy nào trôi nổi - phương thức và chú thích lớp của SpringSandwich dễ dàng tồn tại trong quá trình tái cấu trúc và làm rõ những gì đang được áp dụng ở đâu. (Tiết lộ: Tôi là tác giả).

http://springsandwich.com/


Trông tuyệt vời! Tôi đã tạo ra một vấn đề yêu cầu SpringSandwich có sẵn trong trung tâm maven để giúp việc sử dụng dễ dàng hơn cho các dự án đang xây dựng theo CI hoặc đang triển khai qua Heroku.
Brendon Dugan

Tuyệt quá. Nó có sẵn trong kho lưu trữ trung tâm maven không? Re - blog springsandwich.com trong trang web của tôi với các tài liệu tham khảo của kho git của bạn và tài liệu tham khảo
Arpan Das

1
SpringSandwich hiện ở Maven Central
Magnus

1

Dưới đây là một cách triển khai mà tôi sử dụng để chặn từng yêu cầu HTTP trước khi nó đi ra ngoài và phản hồi quay trở lại. Với việc triển khai này, tôi cũng có một điểm duy nhất mà tôi có thể chuyển bất kỳ giá trị tiêu đề nào với yêu cầu.

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

Dưới đây là Bean Template Rest

@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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.