# WebFlux 支持

## WebFlux 支持

WebFlux Spring 集成模块（`spring-integration-webflux`）允许以反应的方式执行 HTTP 请求和处理入站 HTTP 请求。

你需要在项目中包含此依赖项：

Maven

```
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-webflux</artifactId>
    <version>5.5.9</version>
</dependency>
```

Gradle

```
compile "org.springframework.integration:spring-integration-webflux:5.5.9"
```

在非基于 Servlet 的服务器配置的情况下，必须包含`io.projectreactor.netty:reactor-netty`依赖项。

WebFlux 支持由以下网关实现组成：`WebFluxInboundEndpoint`和`WebFluxRequestExecutingMessageHandler`。该支持完全基于 Spring [WebFlux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux)和[项目反应堆](https://projectreactor.io/)的基础。有关更多信息，请参见[HTTP 支持](./http.html#http)，因为许多选项是在反应性和常规 HTTP 组件之间共享的。

### WebFlux 名称空间支持

Spring 集成提供了`webflux`名称空间和相应的模式定义。要将其包含在配置中，请在应用程序上下文配置文件中添加以下名称空间声明：

```
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:int="http://www.springframework.org/schema/integration"
  xmlns:int-webflux="http://www.springframework.org/schema/integration/webflux"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration
    https://www.springframework.org/schema/integration/spring-integration.xsd
    http://www.springframework.org/schema/integration/webflux
    https://www.springframework.org/schema/integration/webflux/spring-integration-webflux.xsd">
    ...
</beans>
```

### WebFlux 入站组件

从版本 5.0 开始，提供了`WebFluxInboundEndpoint`的`WebHandler`实现。这个组件类似于基于 MVC 的`HttpRequestHandlingEndpointSupport`，它通过新提取的`BaseHttpInboundEndpoint`与该组件共享一些公共选项。它被用于 Spring WebFlux 反应环境（而不是 MVC）。下面的示例展示了 WebFlux 端点的一个简单实现：

爪哇 DSL

```
@Bean
public IntegrationFlow inboundChannelAdapterFlow() {
    return IntegrationFlows
        .from(WebFlux.inboundChannelAdapter("/reactivePost")
            .requestMapping(m -> m.methods(HttpMethod.POST))
            .requestPayloadType(ResolvableType.forClassWithGenerics(Flux.class, String.class))
            .statusCodeFunction(m -> HttpStatus.ACCEPTED))
        .channel(c -> c.queue("storeChannel"))
        .get();
}
```

Kotlin DSL

```
@Bean
fun inboundChannelAdapterFlow() =
    integrationFlow(
        WebFlux.inboundChannelAdapter("/reactivePost")
            .apply {
                requestMapping { m -> m.methods(HttpMethod.POST) }
                requestPayloadType(ResolvableType.forClassWithGenerics(Flux::class.java, String::class.java))
                statusCodeFunction { m -> HttpStatus.ACCEPTED }
            })
    {
        channel { queue("storeChannel") }
    }
```

爪哇

```
@Configuration
@EnableWebFlux
@EnableIntegration
public class ReactiveHttpConfiguration {

    @Bean
    public WebFluxInboundEndpoint simpleInboundEndpoint() {
        WebFluxInboundEndpoint endpoint = new WebFluxInboundEndpoint();
        RequestMapping requestMapping = new RequestMapping();
        requestMapping.setPathPatterns("/test");
        endpoint.setRequestMapping(requestMapping);
        endpoint.setRequestChannelName("serviceChannel");
        return endpoint;
    }

    @ServiceActivator(inputChannel = "serviceChannel")
    String service() {
        return "It works!";
    }

}
```

XML

```
<int-webflux:inbound-gateway request-channel="requests" path="/sse">
    <int-webflux:request-mapping produces="text/event-stream"/>
</int-webflux:inbound-gateway>
```

该配置类似于`HttpRequestHandlingEndpointSupport`（在示例之前提到），只是我们使用`@EnableWebFlux`将 WebFlux 基础架构添加到我们的集成应用程序中。此外，`WebFluxInboundEndpoint`通过使用反作用 HTTP 服务器实现提供的基于按需的能力，对下游流执行`sendAndReceive`操作。

|   |应答部分也是非阻塞的，并且基于内部`FutureReplyChannel`，该内部`Mono`被平映到一个响应`Mono`以进行按需解析。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------|

你可以使用自定义`ServerCodecConfigurer`、`RequestedContentTypeResolver`甚至`ReactiveAdapterRegistry`来配置`WebFluxInboundEndpoint`。后者提供了一种机制，你可以使用该机制以任何反应类型返回回复：reactor`Flux`，rxjava`Observable`，`Flowable`，以及其他类型。通过这种方式，我们可以使用 Spring 集成组件实现[服务器发送事件](https://en.wikipedia.org/wiki/Server-sent_events)场景，如下例所示：

爪哇 DSL

```
@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();
}
```

Kotlin DSL

```
@Bean
fun sseFlow() =
     integrationFlow(
            WebFlux.inboundGateway("/sse")
                       .requestMapping(m -> m.produces(MediaType.TEXT_EVENT_STREAM_VALUE)))
            {
                 handle { (p, h) -> Flux.just("foo", "bar", "baz") }
            }
```

Java

```
@Bean
public WebFluxInboundEndpoint webfluxInboundGateway() {
    WebFluxInboundEndpoint endpoint = new WebFluxInboundEndpoint();
    RequestMapping requestMapping = new RequestMapping();
    requestMapping.setPathPatterns("/sse");
    requestMapping.setProduces(MediaType.TEXT_EVENT_STREAM_VALUE);
    endpoint.setRequestMapping(requestMapping);
    endpoint.setRequestChannelName("requests");
    return endpoint;
}
```

XML

```
<int-webflux:inbound-channel-adapter id="reactiveFullConfig" channel="requests"
                               path="test1"
                               auto-startup="false"
                               phase="101"
                               request-payload-type="byte[]"
                               error-channel="errorChannel"
                               payload-expression="payload"
                               supported-methods="PUT"
                               status-code-expression="'202'"
                               header-mapper="headerMapper"
                               codec-configurer="codecConfigurer"
                               reactive-adapter-registry="reactiveAdapterRegistry"
                               requested-content-type-resolver="requestedContentTypeResolver">
            <int-webflux:request-mapping headers="foo"/>
            <int-webflux:cross-origin origin="foo" method="PUT"/>
            <int-webflux:header name="foo" expression="'foo'"/>
</int-webflux:inbound-channel-adapter>
```

有关更多可能的配置选项，请参见[请求映射支持](./http.html#http-request-mapping)和[跨源资源共享支持](./http.html#http-cors)。

当请求主体为空或`payloadExpression`返回`null`时，请求参数（`MultiValueMap<String, String>`）用于处理目标消息的`payload`。

#### 有效载荷验证

从版本 5.2 开始，`WebFluxInboundEndpoint`可以配置为`Validator`。与[HTTP 支持](./http.html#http-validation)中的 MVC 验证不同，它用于验证`Publisher`中的元素，在执行回退和`payloadExpression`函数之前，`Publisher`已将请求转换为该元素。该框架不能假设在构建最终有效负载之后`Publisher`对象的复杂程度。如果需要限制最终有效负载（或其`Publisher`元素）的验证可见性，则验证应该向下游进行，而不是向 WebFlux 端点进行。有关更多信息，请参见 Spring WebFlux[文件](https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/web-reactive.html#webflux-fn-handler-validation)。使用`IntegrationWebExchangeBindException`（一个`WebExchangeBindException`扩展）拒绝无效的负载，该扩展包含所有验证`Errors`。请参阅 Spring Framework[参考手册](https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation)中有关验证的更多内容。

### WebFlux 出站组件

`WebFluxRequestExecutingMessageHandler`（从版本 5.0 开始）实现类似于`HttpRequestExecutingMessageHandler`。它使用了 Spring Framework WebFlux 模块中的`WebClient`。要对其进行配置，请定义一个类似于以下内容的 Bean:

Java DSL

```
@Bean
public IntegrationFlow outboundReactive() {
    return f -> f
        .handle(WebFlux.<MultiValueMap<String, String>>outboundGateway(m ->
                UriComponentsBuilder.fromUriString("http://localhost:8080/foo")
                        .queryParams(m.getPayload())
                        .build()
                        .toUri())
                .httpMethod(HttpMethod.GET)
                .expectedResponseType(String.class));
}
```

Kotlin DSL

```
@Bean
fun outboundReactive() =
    integrationFlow {
        handle(
            WebFlux.outboundGateway<MultiValueMap<String, String>>({ m ->
                UriComponentsBuilder.fromUriString("http://localhost:8080/foo")
                    .queryParams(m.getPayload())
                    .build()
                    .toUri()
            })
                .httpMethod(HttpMethod.GET)
                .expectedResponseType(String::class.java)
        )
    }
```

Java

```
@ServiceActivator(inputChannel = "reactiveHttpOutRequest")
@Bean
public WebFluxRequestExecutingMessageHandler reactiveOutbound(WebClient client) {
    WebFluxRequestExecutingMessageHandler handler =
        new WebFluxRequestExecutingMessageHandler("http://localhost:8080/foo", client);
    handler.setHttpMethod(HttpMethod.POST);
    handler.setExpectedResponseType(String.class);
    return handler;
}
```

XML

```
<int-webflux:outbound-gateway id="reactiveExample1"
    request-channel="requests"
    url="http://localhost/test"
    http-method-expression="headers.httpMethod"
    extract-request-payload="false"
    expected-response-type-expression="payload"
    charset="UTF-8"
    reply-timeout="1234"
    reply-channel="replies"/>

<int-webflux:outbound-channel-adapter id="reactiveExample2"
    url="http://localhost/example"
    http-method="GET"
    channel="requests"
    charset="UTF-8"
    extract-payload="false"
    expected-response-type="java.lang.String"
    order="3"
    auto-startup="false"/>
```

`WebClient``exchange()`操作返回一个`Mono<ClientResponse>`，将其映射（通过使用几个`Mono.map()`步骤）到一个`AbstractIntegrationMessageBuilder`作为`WebFluxRequestExecutingMessageHandler`的输出。连同`ReactiveChannel`作为`outputChannel`，`Mono<ClientResponse>`评估将被推迟，直到进行下游订阅。否则，它将被视为`async`模式，并且对于来自`WebFluxRequestExecutingMessageHandler`的异步回复，将`Mono`响应调整为`SettableListenableFuture`。输出消息的目标负载取决于`WebFluxRequestExecutingMessageHandler`配置。`setExpectedResponseType(Class<?>)`或`setExpectedResponseTypeExpression(Expression)`标识响应主体元素转换的目标类型。如果`replyPayloadToFlux`被设置为`true`，则响应主体将转换为`Flux`，并为每个元素提供`expectedResponseType`，然后将`Flux`作为下游的有效负载发送。之后，你可以使用[splitter](./splitter.html#splitter)以反应式的方式迭代这个`Flux`。

此外，可以将`BodyExtractor<?, ClientHttpResponse>`注入到`WebFluxRequestExecutingMessageHandler`而不是`expectedResponseType`和`replyPayloadToFlux`属性中。它可以用于对`ClientHttpResponse`的低级访问以及对 body 和 HTTP 头转换的更多控制。 Spring 集成提供了`ClientHttpResponseBodyExtractor`作为标识函数来产生（下游）整个`ClientHttpResponse`和任何其他可能的自定义逻辑。

从版本 5.2 开始，`WebFluxRequestExecutingMessageHandler`支持反应式`Publisher`、`Resource`和`MultiValueMap`类型作为请求消息有效负载。内部使用相应的`BodyInserter`来将其填充到`WebClient.RequestBodySpec`中。当有效负载是反应性的`Publisher`时，可以使用配置的`publisherElementType`或`publisherElementTypeExpression`来确定发布者元素类型的类型。表达式必须解析为`Class<?>`，`String`，这将解析为目标`Class<?>`或`ParameterizedTypeReference`。

从版本 5.5 开始，`WebFluxRequestExecutingMessageHandler`公开了一个`extractResponseBody`标志（默认情况下是`true`），以仅返回响应主体，或返回整个`ResponseEntity`作为回复消息有效负载，而不依赖于所提供的`expectedResponseType`或`replyPayloadToFlux`。如果`ResponseEntity`中不存在主体，则忽略此标志，并返回整个`ResponseEntity`。

有关更多可能的配置选项，请参见[HTTP 出站组件](./http.html#http-outbound)。

### WebFlux 标头映射

由于 WebFlux 组件完全基于 HTTP 协议，因此在 HTTP 头映射中没有区别。参见[HTTP 头映射](./http.html#http-header-mapping)，以获得更多用于映射标头的可能选项和组件。
