Spring Boot添加Http请求拦截器


107

在Spring Boot应用程序中添加HttpRequest拦截器的正确方法是什么?我想做的是记录每个HTTP请求的请求和响应。

Spring Boot文档完全没有涵盖该主题。(http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/

我发现了一些有关如何对较早版本的spring执行相同操作的Web示例,但这些示例与applicationcontext.xml一起使用。请帮忙。


嗨@ riship89 ...我已经HandlerInterceptor成功实现了。运行正常。唯一的问题是,某些方法internal HandlerInterceptor在由处理之前引发了异常custom HandlerInterceptor。在afterCompletion()HandlerInterceptor的内部实现引发错误之后,将调用被覆盖的方法。您对此有解决方案吗?
Chetan Oswal

Answers:


155

由于您使用的是Spring Boot,因此我假设您希望在可能的情况下依靠Spring的自动配置。要添加其他自定义配置(例如拦截器),只需提供一个配置或的bean WebMvcConfigurerAdapter

这是一个配置类的例子:

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

注意,如果要保留mvc的Spring Boots自动配置,请不要使用@EnableWebMvc进行注释。


真好!看起来挺好的!在registry.addInterceptor(...)内部有什么例子?只是好奇地知道“ ...”的样本
riship89 2015年

6
在yourInjectedInceptor上使用@Component注释
Paolo Biavati


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中

从5.0开始,@ deprecated {@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

要将拦截器添加到Spring Boot应用程序,请执行以下操作

  1. 创建一个拦截器类

    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. 而已。现在,您所有的请求都将通过MyCustomInterceptor的preHandle()方法定义的逻辑。


1
我遵循这种方式来拦截到我的应用程序的注册请求,以便进行一些常见的验证。但是问题是我得到了getReader() has already been called for this request错误。有没有更简单的方法可以解决此问题而不使用实际请求的副本?
vigamage '17

调用预处理程序时,请求主体不可用,而只有参数可用。要在请求主体上进行验证,首选方式是使用Aspect J并创建Advice
Hima

12

由于对此的所有响应都使用了现已弃用的抽象WebMvcConfigurer适配器而不是WebMvcInterface适配器(已由@sebdooe指出),因此,这是一个带有Interceptor的SpringBoot(2.1.4)应用程序的最小工作示例:

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

如广告所示

输出将为您提供以下信息:

> 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实现所有方法,对吗?
史蒂文

不,它的(Java 8)接口带有空的默认实现
Tom

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 ,其他方法实现为默认方法。在不赞成使用的类中方便地记录了这种推理。
鲍勃

7

您可能还考虑使用开源的SpringSandwich库,该库使您可以在Spring Boot控制器中直接注释要应用的拦截器,与注释URL路由的方式几乎相同。

这样,就不会出现容易打错字串的情况-SpringSandwich的方法和类注释很容易在重构后幸存下来,并清楚说明了将在哪里应用。(披露:我是作者)。

http://springsandwich.com/


看起来很棒!我已经创建了一个问题,要求在Maven Central中使用SpringSandwich,以使其更易于用于在CI下构建或通过Heroku部署的项目。
布伦登·杜甘

大。它在Maven中央存储库中可用吗?在您的git存储库和参考文献中,在我的网站上重新撰写springsandwich.com博客
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模板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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.