Spring函数式端点返回404问题排查求助
问题场景
开发同时包含常规端点和函数式端点的Spring应用,常规端点可正常返回结果,但所有函数式端点均返回404。已按照Handler+Router模式实现,相关代码及日志如下:
Handler代码
@Service public class LorcCardHandler { private final LorcCardReactiveRepository lorcCardReactiveRepository; @Autowired public LorcCardHandler(LorcCardReactiveRepository lorcCardReactiveRepository) { this.lorcCardReactiveRepository = lorcCardReactiveRepository; } public Mono<ServerResponse> getCardProjectionList(ServerRequest request) { return ServerResponse.ok().body(lorcCardReactiveRepository.getAllCardsProjection(), CardProjection.class); } }
Router代码
@Configuration public class LorcRouter { private final LorcCardHandler handler; public LorcRouter(LorcCardHandler handler) { this.handler = handler; } @Bean public RouterFunction<ServerResponse> getCardListByRoute() { return route().path("/lorc", builder -> builder.GET(handler::getCardProjectionList)).build(); } }
关键日志信息
Bean初始化日志
o.s.b.f.s.DefaultListableBeanFactory : Autowiring by type from beaame 'reactiveMongoTemplate' via factory method to beaamed 'mappingMongoConverter'
o.s.d.r.c.s.RepositoryFactorySuppo: Initializing repository instance for com.luizgomendes.lorc.rest.repository.LorcCardReactiveRepository…
o.s.d.r.c.s.RepositoryFactorySuppo: Finished creation of repository instance for com.luizgomendes.lorc.rest.repository.LorcCardReactiveRepository.
o.s.b.f.s.DefaultListableBeanFactory : Autowiring by type from beaame 'lorcReactiveService' via constructor to beaamed 'lorcCardReactiveRepository'
o.s.b.f.s.DefaultListableBeanFactory : Autowiring by type from beaame 'lorcReactiveController' via constructor to beaamed 'lorcReactiveService'
o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'lorcRouter'
o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'lorcCardHandler'
o.s.b.f.s.DefaultListableBeanFactory : Autowiring by type from beaame 'lorcCardHandler' via constructor to beaamed 'lorcCardReactiveRepository'
o.s.b.f.s.DefaultListableBeanFactory : Autowiring by type from beaame 'lorcRouter' via constructor to beaamed 'lorcCardHandler'
o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'globalExceptionHandler'
o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'getCardListByRoute'
服务调用日志
[Lorcana Rest Services] [nio-8080-exec-1] o.s.web.servlet.DispatcherServle : GET "/lorc", parameters={}
[Lorcana Rest Services] [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
[Lorcana Rest Services] [nio-8080-exec-1] o.s.w.s.r.ResourceHttpRequestHandl: Resourcot found
[Lorcana Rest Services] [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.servlet.resource.NoResourceFoundException: No static resource lorc.]
[Lorcana Rest Services] [nio-8080-exec-1] o.s.web.servlet.DispatcherServle : Completed 404 NOT_FOUND
问题分析
从服务调用日志可见,请求被映射到静态资源处理器,而非函数式路由。核心原因:
- 日志中出现
o.s.web.servlet.DispatcherServle(Servlet栈的DispatcherServlet),而非WebFlux的org.springframework.web.reactive.DispatcherHandler,说明当前应用运行在Servlet栈环境中,WebFlux的RouterFunction不会被Servlet容器处理。 - 通常是因为项目同时引入了
spring-boot-starter-web(Spring MVC)和spring-boot-starter-webflux依赖,Spring Boot默认优先启用Servlet栈,导致WebFlux路由失效。
解决方案
清理依赖
检查项目的pom.xml(Maven)或build.gradle(Gradle),移除spring-boot-starter-web依赖,仅保留spring-boot-starter-webflux。Maven示例:
<!-- 移除该依赖 --> <!-- <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> --> <!-- 保留WebFlux依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>验证RouterFunction定义
确保路由路径配置正确,可调整Router代码写法,显式指定路径匹配规则:@Bean public RouterFunction<ServerResponse> getCardListByRoute() { // 直接匹配GET /lorc return route(GET("/lorc"), handler::getCardProjectionList); }或使用path分组的写法(需明确子路径):
@Bean public RouterFunction<ServerResponse> getCardListByRoute() { return route().path("/lorc", builder -> builder.GET("", handler::getCardProjectionList) // 子路径为空,对应/lorc ).build(); }重启应用验证
调整依赖和代码后,重启应用,再次请求/lorc,检查日志是否出现DispatcherHandler相关的路由匹配日志,确认请求是否正确映射到函数式端点。
内容的提问来源于stack exchange,提问作者Luiz Gustavo Mendes

