172

What is the right way to add HttpRequest interceptors in spring boot application? What I want to do is log requests and responses for every http request.

Spring boot documentation does not cover this topic at all. (http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/)

I found some web samples on how to do the same with older versions of spring, but those work with applicationcontext.xml. Please help.

2
  • Hi @riship89... I have implemented HandlerInterceptor successfully. It is working fine. Only the problem is, some internal HandlerInterceptor throws an exception before it is handled by the custom HandlerInterceptor. The afterCompletion() method which is overridden is called after the error is thrown by the internal implementation of HandlerInterceptor. Do you have solution for this? Commented May 24, 2020 at 13:55
  • For future readers, here is the awesome video tutorial that explains how Spring interceptor works youtube.com/watch?v=DuMf8Nwb-9w Commented Aug 6, 2023 at 23:04

10 Answers 10

197

Since you're using Spring Boot, I assume you'd prefer to rely on Spring's auto configuration where possible. To add additional custom configuration like your interceptors, just provide a configuration or bean of WebMvcConfigurerAdapter.

Here's an example of a config class:

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

NOTE do not annotate this with @EnableWebMvc, if you want to keep Spring Boots auto configuration for mvc.

Sign up to request clarification or add additional context in comments.

15 Comments

use @Component annotation on yourInjectedInceptor
I get error The type WebMvcConfigurerAdapter is deprecated. I am using Spring Web MVC 5.0.6
In Spring 5, just implement WebMvcConfigurer instead of extending WebMvcConfigurerAdapter. Since Java 8 interfaces allow default implementations, it is no longer required to use the adapter (which is why it's deprecated).
thanks for the call out to not add @EnableWebMvc. it was screwing up my spring-boot app
|
150

WebMvcConfigurerAdapter will be deprecated with Spring 5. From its Javadoc:

@deprecated as of 5.0 {@link WebMvcConfigurer} has default methods (made possible by a Java 8 baseline) and can be implemented directly without the need for this adapter

As stated above, what you should do is implementing WebMvcConfigurer and overriding addInterceptors method.

@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyCustomInterceptor()); } } 

2 Comments

Your answer is incomplete because it's missing the implementation of MyCustomInterceptor
@AndriiDzhyrma, you have to write code that is comprehensible to a total beginner. Have a look at this complete answer and you'll get my point. stackoverflow.com/a/42113069/4187541
51

To add interceptor to a spring boot application, do the following

  1. Create an interceptor class

    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. Define a configuration class

    @Configuration public class MyConfig extends WebMvcConfigurerAdapter{ @Override public void addInterceptors(InterceptorRegistry registry){ registry.addInterceptor(new MyCustomInterceptor()).addPathPatterns("/**"); } } 
  3. Thats it. Now all your requests will pass through the logic defined under preHandle() method of MyCustomInterceptor.

2 Comments

I have followed this way to intercept the signup requests come to my application in order to do some common validations. But the problem is i get the getReader() has already been called for this request error. Is there any simpler way to get over this without using a copy of the actual request ?
When the pre-handler is called, the request body is not available, but only parameters, To do validation on the request body, the prefered way will be to use Aspect J and create Advice
47

Since all responses to this make use of the now long-deprecated abstract WebMvcConfigurer Adapter instead of the WebMvcInterface (as already noted by @sebdooe), here is a working minimal example for a SpringBoot (2.1.4) application with an 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"); } } 

works as advertised

The output will give you something like:

> 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 

2 Comments

But this will require you to implement all methods from the WebMvcConfigurer, right?
No, its a (Java 8) interface with empty default implementations
20

I found a good tutorial on this site on how to add request interceptors to specific controllers using annotations:

  1. Define the annotation
  2. Define the interceptor
  3. Add the interceptor to the path
  4. Use the annotation on the specific controller

https://programmer.group/how-do-spring-boot-2.x-add-interceptors.html

I know this question was how to add interceptors to all requests and that's answered already. I was searching the solution to add request interceptors to specific controllers using annotations but couldn't find a solution in stackoverflow. Decided add this content to this question instead of asking a new question.

Define the annotation NeedLogin.class

package com.example.helloSpringBoot.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface NeedLogin { } 

Then define the inceptor class

package com.example.helloSpringBoot.config; import com.example.helloSpringBoot.annotation.NeedLogin; import com.example.helloSpringBoot.util.WxUserInfoContext; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Logon interceptor * * @Author: Java Fragment */ @Component public class LoginInterceptor implements HandlerInterceptor { //This method is executed before accessing the interface. We only need to write the business logic to verify the login status here to verify the login status before the user calls the specified interface. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { NeedLogin needLogin = ((HandlerMethod) handler).getMethodAnnotation(NeedLogin.class); if (null == needLogin) { needLogin = ((HandlerMethod) handler).getMethod().getDeclaringClass() .getAnnotation(NeedLogin.class); } // Check login if you have login validation annotations if (null != needLogin) { WxUserInfoContext curUserContext = (WxUserInfoContext) request.getSession() .getAttribute("curUserContext"); //If session No, not logged in. if (null == curUserContext) { response.setCharacterEncoding("UTF-8"); response.getWriter().write("Not logged in!"); return false; } } } return true; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } } 

Then add the inceptor into the WebConfig

package com.example.helloSpringBoot.config; 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; /** * WebConfig * * @Author: Java Fragment * */ @Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { // Custom interceptor, add intercept path and exclude intercept path registry.addInterceptor(loginInterceptor).addPathPatterns("/**"); } } 

Finally you are free to use the new interceptor using the new annotation @NeedLogin

package com.example.helloSpringBoot.controller; import com.example.helloSpringBoot.annotation.NeedLogin; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { /** * Testing does not require login * * */ @RequestMapping("/testNoLogin") public String testNoLogin (){ return "The call is successful, this interface does not need login validation!-Java Broken read!"; } /** * Testing requires login * * */ @NeedLogin @RequestMapping("/testNeedLogin") public String testNeedLogin (){ return "testNeedLogin!"; } } 

Comments

16

I had the same issue of WebMvcConfigurerAdapter being deprecated. When I searched for examples, I hardly found any implemented code. Here is a piece of working code.

create a class that extends 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; } } 

then Implement WebMvcConfigurer interface

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

3 Comments

how can u only override one method on an interface without compilation issues?
@xetra11 I am also trying to see if we can only implement one method instead of all other methods not used in this case. Is it possible? Did you figured that out?
@arjun The others are implemented as default methods thanks to Java 8. This reasoning is, conveniently, documented in the deprecated class.
7

You might also consider using the open source SpringSandwich library which lets you directly annotate in your Spring Boot controllers which interceptors to apply, much in the same way you annotate your url routes.

That way, no typo-prone Strings floating around -- SpringSandwich's method and class annotations easily survive refactoring and make it clear what's being applied where. (Disclosure: I'm the author).

http://springsandwich.com/

4 Comments

That looks awesome! I've created an issue asking for SpringSandwich to be available in maven central to make it easier to use for projects that are building under CI or which are deploying via Heroku.
Great. Is it available in maven central repository? Re - blogging springsandwich.com in my website with the reference of your git repository and reference
SpringSandwich is now in Maven Central
Looks like this library has been abandonned since 2017
2

For tracking all the request response in the spring-boot (java) application you can create a filter class like this -

import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.util.ContentCachingResponseWrapper; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Objects; import java.util.stream.Collectors; @Component public class RequestResponseTracker extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request); ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response); filterChain.doFilter(requestWrapper, responseWrapper); System.out.println("Request URI: " + request.getRequestURI()); System.out.println("Request Headers: " + Collections.list(request.getHeaderNames()).stream() .map(headerName -> headerName + " -> " + request.getHeader(headerName)).collect(Collectors.toList())); System.out.println("Request Method: " + request.getMethod()); System.out.println("Request Body: " + getStringValue(requestWrapper.getContentAsByteArray(), responseWrapper.getCharacterEncoding())); System.out.println("Response Code: " + response.getStatus()); System.out.println("Response Body: " + getStringValue(responseWrapper.getContentAsByteArray(), responseWrapper.getCharacterEncoding())); System.out.println("Response Headers: " + response.getHeaderNames().stream() .map(headerName -> headerName + " -> " + response.getHeader(headerName)).collect(Collectors.toList())); responseWrapper.copyBodyToResponse(); // Don't forget to add this at the end } private String getStringValue(byte[] contentAsByteArray, String characterEncoding) { try { return new String(contentAsByteArray, 0, contentAsByteArray.length, Objects.nonNull(characterEncoding) ? characterEncoding : StandardCharsets.UTF_8.name()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return ""; } } 

Comments

1

I will recommend the following ways to dynamically inject custom interceptors.
You don't need to add interceptors one by one.

CustomInterceptor:

public interface CustomInterceptor extends HandlerInterceptor { default String[] excludePathPatterns() { return new String[]{ "/*/*.html", "/*/*.css", "/*/*.js", "/*/*.png", "/*/*.xml", "/*/*.json", "/*/*.yaml", "/*/*.yml", "/swagger*/**" }; } default String[] pathPatterns() { return new String[]{"/**"}; } } 

WebMvcConfig:

@Slf4j @Configuration @RequiredArgsConstructor @SuppressWarnings("NullableProblems") @ConditionalOnProperty(name = AutoConfigConstants.ENABLE_MVC, havingValue = "true") public class DefaultMvcAutoConfiguration implements WebMvcConfigurer { static { log.info(AutoConfigConstants.LOADING_MVC_AUTO_CONFIGURE); } private final List<CustomInterceptor> customInterceptors; @Override public void addInterceptors(InterceptorRegistry registry) { WebMvcConfigurer.super.addInterceptors(registry); if (CollectionUtils.isNotEmpty(customInterceptors)) { customInterceptors.forEach(customInterceptor -> registry.addInterceptor(customInterceptor) .addPathPatterns(customInterceptor.pathPatterns()) .excludePathPatterns(customInterceptor.excludePathPatterns())); } } } 

Comments

-1

Below is an implementation I use to intercept each HTTP request before it goes out and the response which comes back. With this implementation, I also have a single point where I can pass any header value with the request.

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

Below is the 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; } 

2 Comments

OK but what you've actually done here is an interceptor FOR RestTemplate (i.e. when YOU make HTTP calls)... not an interceptor for Spring REST Controllers (i.e. when HTTP calls are made against your Spring app/REST-webservices).
The question is about HTTP calls to your controller not HTTP calls from your application which RestTemplate handles. Thanks to @maxxyme comment.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.