spring-integration: Webflux inboundGateway 未映射到请求

spring-integration: Webflux inboundGateway not mapped to request

我已经使用生成 text/event-stream.

的 /sse webflux 资源设置了示例应用程序

当我尝试使用 curl 调用资源时,当我请求时不接受 header 时得到 404,当我请求时接受 header 时得到 406,如下所示:

curl -v http://localhost:8080/events
curl -H "Accept: text/event-stream" -v http://localhost:8080/events

到目前为止我无法让它工作。

我将以下示例代码用于 Webflux.inboundGateway(以及其他工作正常的 Http.inboundGateway 流程):

@SpringBootApplication
@EnableIntegration
@EnableWebFlux
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Bean
  public IntegrationFlow sseFlow() {
      return IntegrationFlows
        .from(WebFlux.inboundGateway("/sse")
                .requestMapping(m -> m.produces(MediaType.TEXT_EVENT_STREAM_VALUE)))
        .handle((p, h) -> Flux.just("foo", "bar", "baz"))
        .get();
  }

  // ... more integration flows, but with Http.inboundGateway

在我的 pom.xml 中,我有这些与 webflux 相关的依赖项:

<dependency>
  <groupId>org.springframework.integration</groupId>
  <artifactId>spring-integration-webflux</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

启动时,我从 WebfluxIntegrationRequestMappingHandlerMapping 收到一条消息,它已映射 /sse(它只说 xIntegrationRequestMappingHandlerMapping 因为 class 名称太长,幸运的是我们看到 x):

2018-05-05 08:08:27.269  INFO 45160 --- [  restartedMain] xIntegrationRequestMappingHandlerMapping : Mapped "{[/sse],methods=[GET || POST],produces=[text/event-stream]}" onto public abstract reactor.core.publisher.Mono<java.lang.Void> org.springframework.web.server.WebHandler.handle(org.springframework.web.server.ServerWebExchange)

现在是有趣的部分。在下面的调试日志中,您可以看到应用程序显然 不考虑 WebfluxIntegrationRequestMappingHandlerMapping,而只考虑其他 HandlerMapping。因此,它选择了 ResourceHttpRequestHandler。

在没有接受的情况下调用时会发生这种情况 header:

2018-05-04 15:59:12.281 DEBUG 36196 --- [nio-8080-exec-3] o.s.b.w.s.f.OrderedRequestContextFilter  : Bound request context to thread: org.apache.catalina.connector.RequestFacade@6b1c36b4
2018-05-04 15:59:12.281 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/sse]
2018-05-04 15:59:12.281 DEBUG 36196 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /sse
2018-05-04 15:59:12.281 DEBUG 36196 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Did not find handler method for [/sse]
2018-05-04 15:59:12.281 DEBUG 36196 --- [nio-8080-exec-3] .IntegrationRequestMappingHandlerMapping : Looking up handler method for path /sse
2018-05-04 15:59:12.282 DEBUG 36196 --- [nio-8080-exec-3] .IntegrationRequestMappingHandlerMapping : Did not find handler method for [/sse]
2018-05-04 15:59:12.282 DEBUG 36196 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping  : Matching patterns for request [/sse] are [/**]
2018-05-04 15:59:12.282 DEBUG 36196 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping  : URI Template variables for request [/sse] are {}
2018-05-04 15:59:12.282 DEBUG 36196 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapping [/sse] to HandlerExecutionChain with handler [ResourceHttpRequestHandler [locations=[class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/], ServletContext resource [/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver@2b3f154f]]] and 1 interceptor
2018-05-04 15:59:12.282 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/sse] is: -1
2018-05-04 15:59:12.282 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2018-05-04 15:59:12.283 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Successfully completed request
2018-05-04 15:59:12.283 DEBUG 36196 --- [nio-8080-exec-3] o.s.b.w.s.f.OrderedRequestContextFilter  : Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@6b1c36b4
2018-05-04 15:59:12.283 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/error]
2018-05-04 15:59:12.283 DEBUG 36196 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /error
2018-05-04 15:59:12.283 DEBUG 36196 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]
2018-05-04 15:59:12.284 DEBUG 36196 --- [nio-8080-exec-3] o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'basicErrorController'
2018-05-04 15:59:12.284 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/error] is: -1
2018-05-04 15:59:12.286 DEBUG 36196 --- [nio-8080-exec-3] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Written [{timestamp=Fri May 04 15:59:12 CEST 2018, status=404, error=Not Found, message=No message available, path=/sse}] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@7d49dc3e]
2018-05-04 15:59:12.286 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2018-05-04 15:59:12.286 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Successfully completed request

当调用 Accept header 时,我在处理 /error 资源时在日志中看到一个额外的警告 Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation

2018-05-05 07:47:19.356 DEBUG 36196 --- [nio-8080-exec-1] o.s.b.w.s.f.OrderedRequestContextFilter  : Bound request context to thread: org.apache.catalina.connector.RequestFacade@6b1c36b4
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/sse]
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /sse
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Did not find handler method for [/sse]
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] .IntegrationRequestMappingHandlerMapping : Looking up handler method for path /sse
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] .IntegrationRequestMappingHandlerMapping : Did not find handler method for [/sse]
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping  : Matching patterns for request [/sse] are [/**]
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping  : URI Template variables for request [/sse] are {}
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapping [/sse] to HandlerExecutionChain with handler [ResourceHttpRequestHandler [locations=[class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/], ServletContext resource [/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver@2b3f154f]]] and 1 interceptor
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/sse] is: -1
2018-05-05 07:47:19.358 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2018-05-05 07:47:19.358 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Successfully completed request
2018-05-05 07:47:19.358 DEBUG 36196 --- [nio-8080-exec-1] o.s.b.w.s.f.OrderedRequestContextFilter  : Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@6b1c36b4
2018-05-05 07:47:19.359 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/error]
2018-05-05 07:47:19.359 DEBUG 36196 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /error
2018-05-05 07:47:19.359 DEBUG 36196 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]
2018-05-05 07:47:19.359 DEBUG 36196 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'basicErrorController'
2018-05-05 07:47:19.359 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/error] is: -1
2018-05-05 07:47:19.360 DEBUG 36196 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolving exception from handler [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
2018-05-05 07:47:19.360 DEBUG 36196 --- [nio-8080-exec-1] .w.s.m.a.ResponseStatusExceptionResolver : Resolving exception from handler [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
2018-05-05 07:47:19.360 DEBUG 36196 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolving exception from handler [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
2018-05-05 07:47:19.360  WARN 36196 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
2018-05-05 07:47:19.360 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2018-05-05 07:47:19.360 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Successfully completed request

为什么Spring不考虑WebfluxIntegrationRequestMappingHandlerMapping?或者这里还有什么问题?

您必须决定您 运行 是响应式应用程序还是 mvc 应用程序。当 spring-boot-starter-web 和 spring-boot-starter-webflux 都被定义为对类路径的依赖时,Spring Boot 默认配置一个 Spring MVC 应用程序.那是我的问题。

我不得不删除 spring-boot-starter-web 和 spring-integration-http 以便 Spring Boot 启动 Netty,而不是 Tomcat。另一种方法是明确告诉 Spring 这样做:

public static void main(String[] args) {
    SpringApplication application = new SpringApplication(WebfluxApplication.class);
    application.setWebApplicationType(WebApplicationType.REACTIVE);
    application.run(WebfluxApplication.class, args);
}

在一个应用程序中混合 spring-integration-http 和 spring-integration-webflux Webflux.inboundGateway 似乎是不可能的,您必须创建两个单独的应用程序,一个启动 Tomcat 用于 servlet 请求和另一个启动 Netty 的请求。

如果您不想切换到反应式应用程序,可能的解决方法: 展示了如何将传入的 http POSTS 路由到反应式发布者,并使用 SSE 通过 @GetMapping MVC 资源将它们作为事件提供。

另见 https://github.com/spring-projects/spring-boot/issues/11025