spring-integration: Webflux inboundGateway не сопоставлен с запросом

Я настроил пример приложения с ресурсом /sse webflux, который создает текст/поток событий.

Когда я пытаюсь вызвать ресурс с помощью curl, я получаю 404 при запросе без заголовка Accept и 406 при запросе с заголовком Accept следующим образом:

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, потому что имя класса такое длинное, к счастью, мы видим 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, а только другие HandlerMappings. В результате вместо этого он выбирает ResourceHttpRequestHandler.

Это происходит при вызове без заголовка Accept:

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 заголовком я вижу дополнительное предупреждение Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation в журнале, пока обрабатывается ресурс /error:

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? Или что еще здесь не так?


person dschulten    schedule 05.05.2018    source источник


Ответы (1)


Вы должны решить, запускаете ли вы реактивное приложение или приложение 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 для запросов сервлета, а другое запускает Netty.

Возможный обходной путь, если вы не хотите переключаться на реактивное приложение: Как создать Spring Reactor Flux из потока интеграции Http? показывает, как направлять входящие http POSTS на реактивный издатель и предоставлять их как события через ресурс @GetMapping MVC с использованием SSE.

Также см. https://github.com/spring-projects/spring-boot/issues/11025.

person dschulten    schedule 05.05.2018
comment
Я все еще задаюсь вопросом: возможно ли создать приложение Spring Boot, которое позволяет одновременно иметь Http.inboundGateway и WebFlux inboundGateway, если я не использую Netty, но, например, Кот? - person dschulten; 05.05.2018