'Spring Interceptor is not compatible with @RepositoryRestResource

@Component
    public class TestInterceptor implements HandlerInterceptor {
        @Override
        public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
            System.out.println("afterCompletion");
        }
        @Override
        public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
            System.out.println("postHandle");
        }
        @Override
        public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
            System.out.println("preHandle");
            return true;
        }
    }

and

@SpringBootConfiguration
public class AnnotationSecurityConfiguration implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TestInterceptor()).addPathPatterns("/api/**");
    }
}

finally the entity is presented as REST using a @RepositoryRestController as follows:

@RepositoryRestResource(excerptProjection = UserSummaryProjection.class)
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}

Then calling

curl -X GET https://localhost:8080/api/v1/users 

but the interceptor is not called.

Because the REST resource is managed Spring Data Rest with @RepositoryRestController the interceptor is not called. But if I write the REST resource with @RestController it will work.

How can I make interceptors work with @RepositoryRestController?



Solution 1:[1]

I got it working (on / context path, and a simple User class, spring-boot v2.4.2) with the following config/spring-boot app:

...
import org.springframework.web.servlet.handler.MappedInterceptor
...    

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
       
    @Bean
    public org.springframework.web.servlet.handler.MappedInterceptor myInterceptor() {
        return new MappedInterceptor(
                new String[]{"/users/**"},  // null => maps to any repository/path
                new MyInterceptorImpl()
        );
    }

    private class MyInterceptorImpl implements HandlerInterceptor  {
         @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("preHandle");
            return true;
        }

        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("postHandle");
        }

        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
           System.out.println("afterCompletion");
        }
    }
}

I used a simplified:

@RepositoryRestResource
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> { }

Solution 2:[2]

you have to add TestInterceptor as a bean and anotate it with @Autowired just add these changes to register the Interceptor into Interceptor Registry :

@Configuration
@SpringBootApplication
public class DemoApplication extends WebMvcConfigurerAdapter {
    @Autowired
    private TestInterceptor testInterceptor;
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(testInterceptor).addPathPatterns("/api/**");
    }

HandlerInterceptor :

@Component
public class TestInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(TestInterceptor.class);
        @Override
        public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
            System.out.println("afterCompletion");
            long startTime = Instant.now().toEpochMilli();
            logger.info("Request URL::" + arg0.getRequestURL().toString() +
                    ":: Start Time=" + Instant.now());
            arg0.setAttribute("startTime", startTime);
        }
        @Override
        public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
            System.out.println("postHandle");
        }
        @Override
        public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
            System.out.println("preHandle");
            return true;
        }
}

as a sample i have changed the '/api/' to '/' and here a log sample as the below :

2021-02-13 00:36:31.075  INFO 14340 --- [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 22 ms
preHandle
postHandle
afterCompletion
2021-02-13 00:36:35.300  INFO 14340 --- [nio-8080-exec-1] com.example.demo.TestInterceptor         : Request URL::http://localhost:8080/time:: Start Time=2021-02-12T22:36:35.289832Z

Solution 3:[3]

It only worked for me, using Spring Boot 2.6.7, by adding a MappedInterceptor.

@Bean
public MappedInterceptor loggingMappedInterceptor(TestInterceptor testInterceptor) {
    return new MappedInterceptor(
            null,  // => maps to any repository
            testInterceptor);
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 KevinC