Spring 拦截器与@RepositoryRestResource 不兼容
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;
}
}
和
@SpringBootConfiguration
public class AnnotationSecurityConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TestInterceptor()).addPathPatterns("/api/**");
}
}
最终使用 @RepositoryRestController 将实体呈现为 REST,如下所示:
@RepositoryRestResource(excerptProjection = UserSummaryProjection.class)
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}
然后调用
curl -X GET https://localhost:8080/api/v1/users
但是没有调用拦截器
因为 REST 资源是管理的 Spring Data Rest 使用 @RepositoryRestController 拦截器没有被调用。但是,如果我使用 @RestController 编写 REST 资源,它将起作用。
如何让拦截器与 @RepositoryRestController 一起工作?
您必须添加 TestInterceptor
作为 bean 并使用 @Autowired
对其进行注释只需添加这些更改以将拦截器注册到拦截器注册表中:
@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/**");
}
处理程序拦截器:
@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;
}
}
作为示例,我已将“/api/”更改为“/”,这里是一个日志示例,如下所示:
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
我让它工作(在 /
上下文路径上,以及一个简单的 User
class、spring-boot v2.4.2)和以下 config/spring-boot 应用程序:
...
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");
}
}
}
我用了一个简化的:
@RepositoryRestResource
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> { }
它只对我有用,使用 Spring Boot 2.6.7,通过添加 MappedInterceptor。
@Bean
public MappedInterceptor loggingMappedInterceptor(TestInterceptor testInterceptor) {
return new MappedInterceptor(
null, // => maps to any repository
testInterceptor);
}
@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;
}
}
和
@SpringBootConfiguration
public class AnnotationSecurityConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TestInterceptor()).addPathPatterns("/api/**");
}
}
最终使用 @RepositoryRestController 将实体呈现为 REST,如下所示:
@RepositoryRestResource(excerptProjection = UserSummaryProjection.class)
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}
然后调用
curl -X GET https://localhost:8080/api/v1/users
但是没有调用拦截器
因为 REST 资源是管理的 Spring Data Rest 使用 @RepositoryRestController 拦截器没有被调用。但是,如果我使用 @RestController 编写 REST 资源,它将起作用。
如何让拦截器与 @RepositoryRestController 一起工作?
您必须添加 TestInterceptor
作为 bean 并使用 @Autowired
对其进行注释只需添加这些更改以将拦截器注册到拦截器注册表中:
@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/**");
}
处理程序拦截器:
@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;
}
}
作为示例,我已将“/api/”更改为“/”,这里是一个日志示例,如下所示:
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
我让它工作(在 /
上下文路径上,以及一个简单的 User
class、spring-boot v2.4.2)和以下 config/spring-boot 应用程序:
...
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");
}
}
}
我用了一个简化的:
@RepositoryRestResource
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> { }
它只对我有用,使用 Spring Boot 2.6.7,通过添加 MappedInterceptor。
@Bean
public MappedInterceptor loggingMappedInterceptor(TestInterceptor testInterceptor) {
return new MappedInterceptor(
null, // => maps to any repository
testInterceptor);
}