这部分文档涵盖了支持反向堆栈,web应用程序,构建在一个 Reactive Streams API上,以运行在非阻塞服务器(如Netty,Undertow和Servlet 3.1+)容器之上。个人章节涵盖了 Spring WebFlux及其功能编程模型对于Servlet堆栈,web应用程序,在Servlet堆栈上去Web

1. Spring WebFlux

1.1.介绍

Spring Framework,Spring Web MVC中包含的原始web框架是为Servlet API和Servlet容器专门构建的。反向堆栈web框架Spring WebFlux在5.0版本中稍后添加。它是完全非阻塞的,支持 反向反向压力,并在诸如Netty,Undertow和Servlet 3.1+容器之类的服务器上运行。

web框架镜像了源模块spring-webmvcspring-webflux的名称, 并在Spring Framework中并存。每个模块都是可选的。应用程序可以使用一个或另一个模块,或者在某些情况下可以使用 - 例如具有反应性WebClient的Spring MVC控制器。

除了web框架,Spring WebFlux还提供了一个用于执行HTTP请求WebClient,用于测试web端点的WebTestClient,以及客户端和服务器反应的WebSocket支持。

1.1.1.为什么一个新的web框架?

答案的一部分是需要一个非阻塞web堆栈来处理少量线程的并发,并以较少的硬件资源进行扩展。Servlet 3.1确实为非阻塞I / O提供了API。但是,使用它将远离Servlet API的其他部分,其中合同是同步的(FilterServlet)或阻塞(getParametergetPart))。这是新的通用API作为任何非阻塞运行时基础的动机。这一点很重要,因为Netty这样的服务器在异步非阻塞空间中已经建立起来了。

答案的另一部分是功能编程。很像Java 5创建的注释添加注释(例如注释的REST控制器或单元测试),在Java 8中添加lambda表达式为Java中的功能API创造了机会。这是非阻塞应用程序和连续样式API的福音 - 由CompletableFutureReactiveX推广,允许异步逻辑的声明性组合。在编程模型级别,Java 8启用了Spring WebFlux,与注释控制器一起提供功能性的web端点。

1.1.2.反应:什么和为什么?

我们触及非阻塞和功能,但为什么反应性,我们的意思是什么?

术语“反应性”是指围绕响应变化构建的编程模型 - 网络组件对I / O事件的响应,UI控制器对鼠标事件的反应等。在这个意义上,非阻塞是反应的,因为不是被阻止现在在作为操作完成或数据变得可用的响应通知的模式。

还有另一个重要的机制,我们在Spring团队与“反应”相关联,这是非阻碍的背压。在同步的命令式代码中,阻止调用可以作为强制呼叫者等待的背部压力的自然形式。在非阻塞代码中,重要的是控制事件的速率,以便快速生产者不会压倒其目的地。

活动流是一种 小规模,也是Java 9中采用的,它定义了具有背压的异步组件之间的交互。例如,作为发布者的数据存储库 可以产生数据,作为订阅者的HTTP服务器 可以写入响应。活动流的主要目的是允许用户控制发布商产生数据的速度或速度。

常见的问题:如果发布商不能放慢下去呢?
活动流的目的只是建立机制和边界。如果发布商不能放慢速度,那么它必须决定是缓冲还是丢弃还是失败。

1.1.3.反应性API

活动流对互操作性起着重要作用。图书馆和基础架构组件感兴趣,但作为应用程序API不太有用,因为它的级别太低。什么应用程序需要更高级和更丰富的功能API来组合异步逻辑 - 类似于Java 8 Stream API,但不仅仅是集合。这是反应库的作用。

Reactor是Spring WebFlux选择的活动库。它提供了 MonoFlux API类型,可以通过与运算符的ReactiveX 词汇对齐的丰富的运算符来处理0..1和0..N的数据序列Reactor是一个反应流库,因此所有的运营商都支持非阻塞的背压。Reactor非常重视服务器端Java。它与Spring密切合作开发。

WebFlux需要Reactor作为核心依赖关系,但它可以通过反应流与其他反应库互操作。作为一般规则,WebFlux API接受纯粹的Publisher输入,在内部适应Reactor类型,使用它们,然后返回FluxMono作为输出。所以您可以传递任何Publisher作为输入,您可以对输出应用操作,但是您需要调整输出以与另一个活动库一起使用。只要有可能 - 例如注释控制器,WebFlux会透明地适应RxJava或其他活动库的使用。有关详细信息,请参阅反应库

1.1.4.编程模型

spring-web模块包含基于Spring WebFlux - HTTP抽象,Reactive Streams服务器适配器,反应式编解码器和核心Web API的反应性基础,其角色与Servlet API相当,但具有非阻塞语义。

在此基础上Spring WebFlux提供了两种编程模型的选择:

  • 注释控制器  - 与Spring MVC一致,并基于与spring-web模块相同的注释。Spring MVC和WebFlux控制器都支持反应(Reactor,RxJava)返回类型,因此不容易将它们分开。一个显着的区别是WebFlux还支持反应式@RequestBody参数。

  • 功能端点  - 基于lambda的轻量级功能编程模型。将其视为小型库或应用程序可用于路由和处理请求的一组实用程序。注释控制器的巨大差异在于,应用程序负责从开始到结束的请求处理,并通过注释声明意图并被回调。

1.1.5.选择web框架

您应该使用Spring MVC还是WebFlux?我们来看几个不同的观点。

如果您的Spring MVC应用程序正常工作,则无需更改。命令编程是编写,理解和调试代码的最简单方法。您最多可以选择库,因为历史上绝大多数都是阻止。

如果您已经购买了非阻塞web堆栈,则Spring WebFlux与此空间中的其他人具有相同的执行模式优势,并提供了可选的服务器 - Netty,Tomcat,Jetty,Undertow,Servlet 3.1 +容器,编程模型的选择 - 注释控制器和功能web端点,以及可选择的反应库 - Reactor,RxJava或其他。

如果您对使用Java 8 lambdas或Kotlin的轻量级功能web框架感兴趣,则使用Spring WebFlux功能web端点。对于具有较少复杂要求的较小应用或微服务,这也可能是更好的选择,可以从更大的透明度和控制中受益。

在微服务架构中,您可以使用Spring MVC或Spring WebFlux控制器或Spring WebFlux功能端点的混合应用程序。在两个框架中支持相同的基于注释的编程模型,使得重新使用知识更容易,同时为正确的工作选择正确的工具。

评估应用程序的一种简单方法是检查其依赖性。如果您有阻止持久性API(JPA,JDBC)或要使用的网络API,那么Spring MVC至少是常见架构的最佳选择。Reactor和RxJava在单独的线程上执行阻塞调用在技术上是可行的,但是您不会充分利用非阻塞web堆栈。

如果您有一个具有呼叫远程服务的Spring MVC应用程序,请尝试反应的WebClient您可以直接从Spring MVC控制器方法返回反应类型(Reactor,RxJava 或其他)。每个呼叫的延迟或呼叫之间的相互依赖性越大,益处越大。Spring MVC控制器也可以调用其他反应组件。

如果你有一个庞大的团队,记住转向非阻塞,功能和声明性编程的陡峭的学习曲线。在没有完全切换的情况下启动的实际方法是使用反应式WebClient除了这个开始之外,测量的好处。我们预计,对于广泛的应用,转移是不必要的。

如果您不确定要查找什么好处,首先了解非阻塞I / O如何工作(例如,单线程Node.js上的并发不是矛盾)及其影响。标签行是“具有较少硬件的缩放”,但不能保证效果,而不是一些网络I / O可能会变慢或不可预测。这个Netflix 博客文章 是一个很好的资源。

1.1.6.选择服务器

Spring WebFlux支持Netty,Undertow,Tomcat,Jetty和Servlet 3.1+容器。每个服务器都适用于一个通用的Reactive Streams API。Spring WebFlux编程模型基于该通用API。

常见的问题:Tomcat和Jetty如何在两个堆栈中使用?
Tomcat和Jetty的核心是无阻拦。这是Servlet API,它添加了一个阻塞的外观。从版本3.1开始,Servlet API为非阻塞I / O添加了一个选择。然而,其使用需要注意避免其他同步和阻塞部件。因此Spring的反应式web堆栈有一个低级Servlet适配器来桥接到活动流,但Servlet API否则不会直接使用。

默认情况下,Spring Boot 2使用Netty WebFlux,因为Netty在异步非阻塞空间中被广泛使用,并且还提供可共享资源的客户端和服务器。通过比较Servlet 3.1非阻塞I / O没有太多的使用,因为使用它的条数是如此之高。Spring WebFlux打开了一条实用的通路。

Spring Boot中的默认服务器选择主要是关于开箱即用的体验。应用程序仍然可以选择任何其他受支持的服务器,这些服务器也对性能进行了高度优化,完全无阻塞,并适应了反应流反向压力。在Spring Boot中,进行切换是微不足道的。

1.1.7.表现与规模

表现有很多特点和意义。反应和非阻塞通常不会使应用程序运行更快。在某些情况下,例如,如果使用WebClient并行执行远程调用,则可以使用它们。总的来说,需要更多的工作来处理非阻塞的方式,并且可以稍微增加所需的处理时间。

反应和非阻塞的关键预期好处是能够以小的固定数量的线程和较少的内存进行扩展。这使得应用程序在负载下更具弹性,因为它们以更可预测的方式扩展。为了观察这些好处,您需要有一些延迟,包括慢速和不可预测的网络I / O的混合。这是反应堆栈开始显示其优势的地方,差异可能很大。

1.2.反应Spring Web

spring-web模块提供低级基础设施和HTTP抽象(客户端和服务器)来构建无反应的web应用程序。所有公共API都围绕着Reactor作为后备实现的活动流构建。

服务器支持分为两层:

  • HttpHandler和服务器适配器 - 用于使用Reactive Streams反向压力的HTTP请求处理的最基本的通用API。

  • WebHandler API  - 具有过滤器链样式处理的稍高级别但仍是通用服务器web API。

1.2.1.的HttpHandler

每个HTTP服务器都有一些API用于HTTP请求处理。 HttpHandler 是一个简单的合同,一种方法来处理请求和响应。这是非常小的。其主要目的是为不同服务器上的HTTP请求处理提供一个通用的基于反应流的API。

spring-web模块包含每个支持的服务器的适配器。下表显示了使用的服务器API以及Reactive Streams支持的位置:

服务器名称 使用服务器API 反应流支持

Netty

Netty API

Reactor Netty

Undertow

Undertow API

spring-web: Undertow to Reactive Streams bridge

Tomcat

Servlet 3.1 non-blocking I/O; Tomcat API to read and write ByteBuffers vs byte[]

spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge

Jetty

Servlet 3.1 non-blocking I/O; Jetty API to write ByteBuffers vs byte[]

spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge

Servlet 3.1 container

Servlet 3.1 non-blocking I/O

spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge

以下是每个服务器的必需依赖关系, 支持的版本和代码段:

服务器名称 组ID 神器名

Reactor Netty

io.projectreactor.ipc

reactor-netty

Undertow

io.undertow

undertow-core

Tomcat

org.apache.tomcat.embed

tomcat-embed-core

Jetty

org.eclipse.jetty

jetty-server, jetty-servlet

Reactor Netty:

HttpHandler handler = ...
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create(host, port).newHandler(adapter).block();

暗潮:

HttpHandler handler = ...
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
server.start();

Tomcat的:

HttpHandler handler = ...
Servlet servlet = new TomcatHttpHandlerAdapter(handler);

Tomcat server = new Tomcat();
File base = new File(System.getProperty("java.io.tmpdir"));
Context rootContext = server.addContext("", base.getAbsolutePath());
Tomcat.addServlet(rootContext, "main", servlet);
rootContext.addServletMappingDecoded("/", "main");
server.setHost(host);
server.setPort(port);
server.start();

码头:

HttpHandler handler = ...
Servlet servlet = new JettyHttpHandlerAdapter(handler);

Server server = new Server();
ServletContextHandler contextHandler = new ServletContextHandler(server, "");
contextHandler.addServlet(new ServletHolder(servlet), "/");
contextHandler.start();

ServerConnector connector = new ServerConnector(server);
connector.setHost(host);
connector.setPort(port);
server.addConnector(connector);
server.start();

您还可以通过将ServletHttpHandlerAdapter的处理程序包装为Servlet来将其部署为任何Servlet 3.1容器。

1.2.2.WebHandler API

HttpHandler是在不同服务器上运行的基础。在该基地WebHandler API提供异常处理器(稍高级别处理链WebExceptionHandler),过滤器(网页过滤),和目标的处理程序(WebHandler)。

所有组件都适用于ServerWebExchange - 用于HTTP请求和响应的容器,该请求和响应还添加请求属性,会话属性,访问表单数据,多部分数据等。

处理链可以与构建HttpHandlerWebHttpHandlerBuilder放在一起,而后者又可以使用服务器适配器运行要使用构建器单独添加组件或指向ApplicationContext以检测以下内容:

豆名 豆类型 计数 描述

"webHandler"

WebHandler

1

过滤后的目标处理程序

<any>

WebFilter

0..N

过滤器

<any>

WebExceptionHandler

0..N

过滤器链之后的异常处理程序

"webSessionManager"

WebSessionManager

0..1

自定义会话管理器 DefaultWebSessionManager默认

"serverCodecConfigurer"

ServerCodecConfigurer

0..1

定制形式和多部分数据解码器; ServerCodecConfigurer.create()默认

"localeContextResolver"

LocaleContextResolver

0..1

自定义解析器LocaleContext; AcceptHeaderLocaleContextResolver默认情况下

1.2.3.编解码器

spring-web模块提供 HttpMessageReaderHttpMessageWriter, 用于使用反应流对HTTP请求和响应体进行编码和解码。它基于spring-core的较低级合同:

  • DataBuffer  - 字节缓冲区的抽象 - 例如Netty ByteBufjava.nio.ByteBuffer

  • 编码器  - 将对象流序列化到数据缓冲区流

  • 解码器  - 将数据缓冲区流反序列化为对象流

spring-core中存在基本的EncoderDecoder实现,但是spring-web为JSON,XML和其他格式添加更多。您可以使用EncoderHttpMessageWriterDecoderHttpMessageReader的任何EncoderDecoder作为阅读器或作者。对于服务器发送的事件,表单数据等,还有一些额外的web只读写器实现。

最后,可以使用ClientCodecConfigurerServerCodecConfigurer初始化读者和作家列表。它们包括支持类路径检测和默认值,以及覆盖或替换这些默认值的能力。

1.3.调度员手机

Spring WebFlux像Spring MVC一样围绕前端控制器模式设计,其中中心WebHandlerDispatcherHandler,提供了一个用于请求处理的共享算法,而实际工作由可配置的委托组件执行。该模型灵活,支持多种工作流程。

DispatcherHandler从Spring配置中发现需要的委托组件。它也设计成一个Spring bean本身和工具ApplicationContextAware访问它的运行环境。如果DispatcherHandler与bean名称申报“webHandler”这是又被发现 WebHttpHandlerBuilder 这将WebHandler API中描述的请求处理链放在一起

WebFlux应用程序中的Spring配置通常包含:

给出WebHttpHandlerBuilder构建处理链的配置:

ApplicationContext context = ...
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context);

所产生的HttpHandler可以与 服务器适配器一起使用

1.3.1.特殊豆类

DispatcherHandler委托给特殊的bean来处理请求并呈现适当的响应。“特殊的bean”是指Spring管理的Object实例,它们实现下表中列出的框架合同之一。Spring WebFlux提供这些合同的内置实现,但您也可以自定义,扩展或替换它们。

表1. ApplicationContext中的特殊bean类型
豆类型 说明

HandlerMapping

Map a request to a handler. The mapping is based on some criteria the details of which vary by HandlerMapping implementation — annotated controllers, simple URL pattern mappings, etc.

HandlerAdapter

Helps the DispatcherHandler to invoke a handler mapped to a request regardless of how the handler is actually invoked. For example invoking an annotated controller requires resolving various annotations. The main purpose of a HandlerAdapter is to shield the DispatcherHandler from such details.

HandlerResultHandler

Process the HandlerResult returned from a HandlerAdapter.

1.3.2.处理顺序

DispatcherHandler处理请求如下:

  • 每个HandlerMapping被要求找到匹配的处理程序,并使用第一个匹配。

  • 如果找到一个处理程序,它将通过一个适当的HandlerAdapter执行,它将执行中的返回值显示为HandlerResult

  • 通过直接写入响应或使用视图进行呈现,HandlerResult被赋予适当的HandlerResultHandler来完成处理。

1.4.注释控制器

Spring WebFlux提供基于注释的编程模型,其中@Controller@RestController组件使用注释来表达请求映射,请求输入,异常处理等。注释控制器具有灵活的方法签名,不必扩展基类或实现特定接口。

这是一个基本的例子:

@RestController
public class HelloController {

        @GetMapping("/hello")
        public String handle() {
                return "Hello WebFlux";
        }
}

在这个例子中,这些方法返回一个要写入响应体的String。

1.4.1.@Controller声明

您可以使用标准的Spring bean定义来定义控制器bean。@Controller构造型允许自动检测,与Spring一般支持检测类路径中的@Component类并为其自动注册bean定义相一致。它也作为注释类的构造型,表示其作为web组件的作用。

要启用自动检测此类@Controller bean,可以将组件扫描添加到Java配置中:

@Configuration
@ComponentScan("org.example.web")
public class WebConfig {

        // ...
}

@RestController是一个组成的注释,它本身用@Controller@ResponseBody注释,表示每个方法都继承了类型级别的@ResponseBody注释,因此写入响应体(对模型和-vew渲染)。

1.4.2.映射请求

@RequestMapping注释用于将请求映射到控制器方法。它具有通过URL,HTTP方法,请求参数,标头和媒体类型匹配的各种属性。它可以在类级别使用来表达共享映射,或者在方法级别缩小到特定的端点映射。

还有@RequestMapping的HTTP方法特定快捷方式:

  • @GetMapping

  • @PostMapping

  • @PutMapping

  • @DeleteMapping

  • @PatchMapping

快捷方式变体是 组合的注释  - 它们自己用@RequestMapping注释。它们通常用于方法级别。在类级别,@RequestMapping对于表达共享映射更有用。

@RestController
@RequestMapping("/persons")
class PersonController {

        @GetMapping("/{id}")
        public Person getPerson(@PathVariable Long id) {
                // ...
        }

        @PostMapping
        @ResponseStatus(HttpStatus.CREATED)
        public void add(@RequestBody Person person) {
                // ...
        }
}
URI模式

您可以使用glob模式和通配符映射请求:

  • ?匹配一个字符

  • *匹配一个路径段中的零个或多个字符

  • **匹配零个或多个路径段

您也可以使用@PathVariable声明URI变量并访问其值:

@GetMapping("/owners/{ownerId}/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
        // ...
}

URI变量可以在类和方法级别声明:

@Controller
@RequestMapping("/owners/{ownerId}")
public class OwnerController {

        @GetMapping("/pets/{petId}")
        public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
                // ...
        }
}

URI变量会自动转换为相应的类型,或者引用“TypeMismatchException”。默认情况下支持简单类型 - intlongDate,您可以注册任何其他数据类型的支持。

URI变量可以显式命名 - 例如@PathVariable("customId"),但是如果名称相同,并且您的代码使用调试信息编译,或者在Java 8上使用-parameters编译器标志,则可以留下该细节。

语法{*varName}声明一个与零个或多个剩余路径段匹配的URI变量。例如/resources/{*path}匹配所有文件/resources/"path"变量捕获完整的相对路径。

语法{varName:regex}使用具有语法{varName:regex}的正则表达式声明URI变量,例如给定URL "/spring-web-3.0.5 .jar",以下方法提取名称,版本和文件扩展名:

@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
public void handle(@PathVariable String version, @PathVariable String ext) {
        // ...
}

URI路径模式还可以嵌入${…​}占位符,通过PropertyPlaceHolderConfigurer在本地,系统,环境和其他属性源启动时解决。这可以用于例如基于某些外部配置参数化基本URL。

Spring WebFlux使用PathPatternPathPatternParser的URI路径匹配支持,这两者都位于spring-web中,并且明确设计用于在web应用程序中的HTTP URL路径,其中大量的URI路径模式在运行时匹配。

Spring WebFlux不支持后缀模式匹配 - 不像Spring MVC,其中映射(如/person)也匹配到/person.*对于基于URL的内容协商,如果需要,我们建议使用一个更简单,更明确,更不易受基于URL路径攻击的查询参数。

模式比较

当多个模式与URL匹配时,必须比较它们以找到最佳匹配。这是通过PathPattern.SPECIFICITY_COMPARATOR来完成的,其中查找更具体的模式。

对于每个模式,根据URI变量和通配符的数量计算得分,其中URI变量的分数低于通配符。总分较低的模式胜出。如果两个模式具有相同的分数,那么选择的时间越长。

捕获所有模式,例如**{*varName},从评分中排除,并且总是最后排序。如果两种模式都是全部的,则选择的时间越长。

耗材介质类型

您可以根据请求的Content-Type缩小请求映射:

@PostMapping(path = "/pets", consumes = "application/json")
public void addPet(@RequestBody Pet pet) {
        // ...
}

消耗属性还支持否定表达式 - 例如!text/plain表示除“text / plain”之外的任何内容类型。

您可以在类级别声明一个共享的消耗属性。与大多数其他请求映射属性不同,当在类级别使用时,方法级消耗属性将覆盖而不是扩展类级别声明。

MediaType为常用媒体类型提供常量,例如APPLICATION_JSON_VALUEAPPLICATION_JSON_UTF8_VALUE

可生产媒体类型

您可以根据Accept请求标头和控制器方法产生的内容类型列表来缩小请求映射:

@GetMapping(path = "/pets/{petId}", produces = "application/json;charset=UTF-8")
@ResponseBody
public Pet getPet(@PathVariable String petId) {
        // ...
}

媒体类型可以指定一个字符集。支持否定表达式 - 例如!text/plain表示除“text / plain”之外的任何内容类型。

您可以在类级别声明共享的生成属性。与大多数其他请求映射属性不同,当在类级别使用时,方法级别生成的属性将覆盖而不是扩展类级别声明。

MediaType为常用媒体类型提供常量,例如APPLICATION_JSON_VALUEAPPLICATION_JSON_UTF8_VALUE

参数和标题

您可以根据查询参数条件缩小请求映射。您可以测试查询参数("myParam")的存在,缺少("!myParam")或特定值("myParam=myValue"):

@GetMapping(path = "/pets/{petId}", params = "myParam=myValue")
public void findPet(@PathVariable String petId) {
        // ...
}

您也可以使用与请求头条件相同的条件:

@GetMapping(path = "/pets", headers = "myHeader=myValue")
public void findPet(@PathVariable String petId) {
        // ...
}
HTTP头,选项

@GetMapping - 还有@RequestMapping(method=HttpMethod.GET),透明地支持HTTP HEAD以进行映射。控制器方法不需要改变。HttpHandler服务器适配器中应用的响应包装器确保将"Content-Length"头设置为写入的字节数,而不实际写入响应。

默认情况下,通过将匹配的URL模式的所有@RequestMapping方法中列出的HTTP方法列表设置为“允许”响应头来处理HTTP选项。

对于没有HTTP方法声明的@RequestMapping,“允许”标头设置为"GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS"控制器方法应始终声明支持的HTTP方法,例如使用HTTP方法特定的变体 - @GetMapping@PostMapping

@RequestMapping方法可以显式地映射到HTTP HEAD和HTTP选项,但这在普通情况下是不必要的。

1.4.3.处理方法

@RequestMapping处理程序方法具有灵活的签名,可以从一系列支持的控制器方法参数和返回值中进行选择。

方法参数

下表显示支持的控制器方法参数。

需要阻塞I / O的参数(例如,读取请求体)要解析的参数支持无效类型(Reactor,RxJava 或其他)。这在描述列中标记。不需要在不需要阻止的参数上使用反应类型。

JDK 1.8的java.util.Optional作为方法参数与具有required属性的注释相结合,例如@RequestParam@RequestHeader等,并且等同于required=false

控制器方法参数 描述

ServerWebExchange

访问用于HTTP请求和响应,请求和会话属性checkNotModified方法等的完整ServerWebExchange - 容器。

ServerHttpRequest, ServerHttpResponse

访问HTTP请求或响应。

WebSession

访问会话; 除非添加属性,否则不会强制新会话的开始。支持反应类型。

java.security.Principal

目前认证用户; 如果已知,可能是一个特定的Principal实现类。支持反应类型。

org.springframework.http.HttpMethod

请求的HTTP方法。

java.util.Locale

当前请求区域设置由最具体的LocaleResolver可用,实际上由配置的LocaleResolver / LocaleContextResolver决定。

Java 6+: java.util.TimeZone
Java 8+: java.time.ZoneId

与当前请求相关联的时区,由LocaleContextResolver确定。

@PathVariable

访问URI模板变量。

@RequestParam

访问Servlet请求参数。参数值将转换为声明的方法参数类型。

@RequestHeader

访问请求头。标题值将转换为声明的方法参数类型。

@RequestBody

访问HTTP请求体。正文内容使用HttpMessageReader转换为声明的方法参数类型。支持反应类型。

HttpEntity<B>

访问请求标头和正文。身体被转换为HttpMessageReader支持反应类型。

@RequestPart

访问“multipart / form-data”请求中的一部分。支持反应类型。

java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap

对于暴露于web视图的隐式模型的访问和更新。

Command or form object (with optional @ModelAttribute)

根据@InitBinder方法和/或HandlerAdapter配置(参见RequestMappingHandlerAdapter上的webBindingInitializer属性),可以通过设置器或直接到字段绑定请求参数的命令对象。

命令对象及其验证结果将作为模型属性公开,默认情况下使用命令类名 - 例如“some.package.OrderAddress”类型的命令对象的model属性“orderAddress”。可以使用@ModelAttribute自定义模型属性名称。

支持反应类型。

Errors, BindingResult

命令/表单对象数据绑定的验证结果; 必须在控制器方法签名后的命令/ form对象之后立即声明此参数。

SessionStatus

用于标记表单处理完成,它触发清除通过类级别@SessionAttributes注释声明的会话属性。

UriComponentsBuilder

为了准备相对于当前请求的主机,端口,方案,上下文路径和servlet映射的字面部分的URL,也考虑到ForwardedX-Forwarded-*标头。

@SessionAttribute

访问任何会话属性; 与通过类级别@SessionAttributes声明的结果存储在会话中的模型属性相反。

@RequestAttribute

访问请求属性。

返回值

下表显示支持的控制器方法返回值。所有返回值都支持无效类型 - Reactor,RxJava 或其他

控制器方法返回值 描述

@ResponseBody

返回值通过HttpMessageWriter编码并写入响应。

HttpEntity<B>, ResponseEntity<B>

返回值指定完整的响应,包括HTTP头和身体通过HttpMessageWriter编码并写入响应。

HttpHeaders

返回一个响应与标题,没有身体。

String

要使用ViewResolver解析并与隐式模型一起使用 - 通过命令对象和@ModelAttribute方法确定的视图名称。处理程序方法还可以通过声明Model参数(见上文)以编程方式丰富模型。

View

用于与隐式模型一起渲染的View实例 - 通过命令对象和@ModelAttribute方法确定。处理程序方法还可以通过声明Model参数(见上文)以编程方式丰富模型。

java.util.Map, org.springframework.ui.Model

要添加到隐式模型中的属性,其视图名称从请求路径隐式确定。

Rendering

用于模型和视图呈现场景的API。

void

用于不写响应体的方法; 或视图名称应该从请求路径隐式确定的方法。

Flux<ServerSentEvent>, Observable<ServerSentEvent>, or other reactive type

发送服务器发送的事件; 只有在需要写入数据的情况下才可以省略SeverSentEvent包装器(但是必须在通过生产属性的映射中请求或声明text/event-stream)。

Any other return type

要添加到具有通过RequestToViewNameTranslator隐式确定的视图名称的隐式模型的单个模型属性; 可以通过方法级@ModelAttribute指定属性名称,否则基于返回类型的类名选择名称。

1.5.功能端点

Spring WebFlux提供了一个轻量级功能的编程模型,其中使用函数来路由和处理请求,以及合同是为不变性设计的。它是基于注释的编程模型的替代,但是运行在相同的 Reactive Spring Web基础上

1.5.1.HandlerFunction

传入的HTTP请求由a处理HandlerFunction,这本质上是一个需要ServerRequest并返回Mono<ServerResponse>的函数。处理函数的注释对应是@RequestMapping方法。

ServerRequestServerResponse是不可变的接口,提供JDK-8友好访问底层的HTTP消息与反动流 非阻塞背压。请求将身体显示为Reactor FluxMono类型; 该响应接受任何反应流Publisher作为主体(请参阅 反应库)。

ServerRequest可以访问各种HTTP请求元素:方法,URI,查询参数,以及 - 通过单独的ServerRequest.Headers接口 - 头。通过body方法提供对身体的访问。例如,这是如何将请求体提取到Mono<String>中:

Mono <String> string = request.bodyToMono(String.class);

这里是如何将身体提取为Flux,其中Person是一个可以从身体内容反序列化的类(即,如果身体包含JSON,则由Jackson支持Person,或JAXB如果是XML)。

Flux <Person> people = request.bodyToFlux(Person.class);

上面的bodyToMonobodyToFlux实际上是使用通用ServerRequest.body(BodyExtractor)方法的便利方法。BodyExtractor是一个功能策略界面,允许您编写自己的提取逻辑,但在BodyExtractors实用程序类中可以找到常见的BodyExtractor实例。所以,上面的例子可以替换为:

Mono <String> string = request.body(BodyExtractors.toMono(String.class);
Flux <Person> people = request.body(BodyExtractors.toFlux(Person.class);

同样,ServerResponse提供对HTTP响应的访问。由于它是不可变的,所以您创建一个具有构建器的ServerResponse构建器允许您设置响应状态,添加响应标题并提供正文。例如,这是如何使用200 OK状态创建响应,JSON内容类型和正文:

Mono <Person> person = ...
。ServerResponse.ok()的contentType(MediaType.APPLICATION_JSON)。体(人);

这里是如何使用201 CREATED状态,"Location"标题和空白体来构建响应:

URI位置= ...
ServerResponse.created(位置).build();

将这些组合在一起可以创建一个HandlerFunction例如,这里是一个简单的“Hello World”处理程序lambda的示例,它返回一个200状态的响应和一个基于String的主体:

HandlerFunction<ServerResponse> helloWorld =
  request -> ServerResponse.ok().body(fromObject("Hello World"));

写作处理函数作为lambda,就像我们上面所说的那样很方便,但是在处理多个函数时可能缺乏可读性,变得不那么容易维护。因此,建议将相关处理函数分组到一个处理程序或控制器类中。例如,这里是一个暴露了一个反应的Person存储库的类:

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;

public class PersonHandler {

        private final PersonRepository repository;

        public PersonHandler(PersonRepository repository) {
                this.repository = repository;
        }

        public Mono<ServerResponse> listPeople(ServerRequest request) { (1)
                Flux<Person> people = repository.allPeople();
                return ServerResponse.ok().contentType(APPLICATION_JSON).body(people, Person.class);
        }

        public Mono<ServerResponse> createPerson(ServerRequest request) { (2)
                Mono<Person> person = request.bodyToMono(Person.class);
                return ServerResponse.ok().build(repository.savePerson(person));
        }

        public Mono<ServerResponse> getPerson(ServerRequest request) { (3)
                int personId = Integer.valueOf(request.pathVariable("id"));
                Mono<ServerResponse> notFound = ServerResponse.notFound().build();
                Mono<Person> personMono = this.repository.getPerson(personId);
                return personMono
                                .flatMap(person -> ServerResponse.ok().contentType(APPLICATION_JSON).body(fromObject(person)))
                                .switchIfEmpty(notFound);
        }
}
1 listPeople是一个处理函数,它将存储库中找到的所有Person对象作为JSON返回。
2 createPerson是一个处理函数,用于存储请求正文中包含的新的Person请注意,PersonRepository.savePerson(Person)返回Mono<Void>:一个空的Mono,当人从请求中读取并存储完成信号时,它会发出一个完成信号。因此,当接收到完成信号时,即Person已被保存,我们使用build(Publisher<Void>)方法发送响应。
3 getPerson是通过路径变量id标识的返回单个人的处理函数。我们通过存储库检索Person,并创建一个JSON响应(如果找到)。如果没有找到,我们使用switchIfEmpty(Mono<T>)返回404 Not Found应答。

1.5.2.RouterFunction

传入请求被路由到处理函数RouterFunction,它是一个占用ServerRequest并返回Mono<HandlerFunction>的函数。如果请求与特定路由匹配,则返回处理函数; 否则返回一个空的MonoRouterFunction@Controller类中的@RequestMapping注释具有相似的用途。

通常,您不要自己编写路由器功能,而是使用RouterFunctions.route(RequestPredicate, HandlerFunction)使用请求谓词和处理函数创建一个。如果谓词适用,请求将路由到给定的处理函数; 否则不执行路由,导致404 Not Found响应。虽然您可以编写自己的RequestPredicate,但您不需要:RequestPredicates实用程序类提供常用的谓词,基于路径,HTTP方法,内容类型等进行匹配。使用route,我们可以路由到我们的“Hello World”处理函数:

RouterFunction<ServerResponse> helloWorldRoute =
        RouterFunctions.route(RequestPredicates.path("/hello-world"),
        request -> Response.ok().body(fromObject("Hello World")));

两个路由器功能可以组成一个新的路由器功能,路由到任一处理函数:如果第一个路由的谓词不匹配,则第二个被评估。组合的路由器功能按顺序进行评估,因此在通用功能之前放置特定功能是有意义的。您可以通过调用RouterFunction.and(RouterFunction)或通过调用RouterFunction.andRoute(RequestPredicate, HandlerFunction)来组合两个路由器功能,这是RouterFunction.and()RouterFunctions.route()的方便组合。

鉴于上面显示的PersonHandler,我们现在可以定义路由器功能,路由到相应的处理函数。我们使用方法引用 来引用处理函数:

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;

PersonRepository repository = ...
PersonHandler handler = new PersonHandler(repository);

RouterFunction<ServerResponse> personRoute =
        route(GET("/person/{id}").and(accept(APPLICATION_JSON)), handler::getPerson)
                .andRoute(GET("/person").and(accept(APPLICATION_JSON)), handler::listPeople)
                .andRoute(POST("/person").and(contentType(APPLICATION_JSON)), handler::createPerson);

除路由器功能之外,还可以通过调用RequestPredicate.and(RequestPredicate)RequestPredicate.or(RequestPredicate)来组成请求谓词。这些工作正如预期的那样:对于and如果两个给定的谓词匹配,则产生的谓词匹配; or如果任一谓词都匹配RequestPredicates中发现的大部分谓词都是组合。例如,RequestPredicates.GET(String)RequestPredicates.method(HttpMethod)RequestPredicates.path(String)的组合。

1.5.3.运行服务器

如何在HTTP服务器中运行路由器功能?一个简单的选择是通过RouterFunctions.toHttpHandler(RouterFunction)将路由器功能转换为HttpHandler然后可以将HttpHandler与多个服务器适配器一起使用。有关服务器的具体说明,请参阅HttpHandler

也可以使用 DispatcherHandler安装程序并行附带注释控制器。最简单的方法是通过 WebFlux Java Config创建必要的配置来处理具有路由器和处理函数的请求。

1.5.4.HandlerFilterFunction

路由器功能映射的路由可以通过调用RouterFunction.filter(HandlerFilterFunction)进行过滤,其中HandlerFilterFunction本质上是一个占用ServerRequestHandlerFunction的函数,并返回一个ServerResponsehandler函数参数表示链中的下一个元素:这通常是路由到的HandlerFunction,但如果应用了多个过滤器,也可以是另一个FilterFunction使用注释,可以使用@ControllerAdvice和/或ServletFilter实现类似的功能。我们给我们的路由添加一个简单的安全过滤器,假设我们有一个SecurityManager可以确定是否允许特定的路径:

import static org.springframework.http.HttpStatus.UNAUTHORIZED;

SecurityManager securityManager = ...
RouterFunction<ServerResponse> route = ...

RouterFunction<ServerResponse> filteredRoute =
        route.filter(request, next) -> {
                if (securityManager.allowAccessTo(request.path())) {
                        return next.handle(request);
                }
                else {
                        return ServerResponse.status(UNAUTHORIZED).build();
                }
  });

在这个例子中可以看到,调用next.handle(ServerRequest)是可选的:我们只允许在允许访问时执行处理函数。

1.6.WebFlux Java配置

WebFlux Java配置提供了适用于大多数应用程序的默认配置以及配置API来进行自定义。有关配置API中不可用的更高级自定义,请参阅高级配置模式

您不需要了解Java配置文件创建的底层Bean,但在WebFluxConfigurationSupport中看起来很容易,如果您想了解更多信息,请参阅 特殊的bean类型

1.6.1.启用配置

在Java配置中使用@EnableWebFlux注释:

@Configuration
@EnableWebFlux
public class WebConfig {
}

以上注册了许多Spring WebFlux 基础架构bean,也适应了类路径中可用的依赖关系 - 用于JSON,XML等。

1.6.2.配置API

在您的Java配置中实现WebFluxConfigurer接口:

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

        // Implement configuration methods...

}

1.6.3.转换,格式化

默认情况下,安装了NumberDate类型的格式化程序,包括对@NumberFormat@DateTimeFormat注释的支持。如果Joda时间存在于类路径上,也将安装完整的Joda Time格式化库的支持。

注册自定义格式化器和转换器:

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

        @Override
        public void addFormatters(FormatterRegistry registry) {
                // ...
        }

}

有关 何时使用FormatterRegistrars的更多信息,请参阅FormatterRegistrar SPIFormattingConversionServiceFactoryBean

1.6.4.验证

默认情况下,如果类路径中存在Bean验证 - 例如Hibernate验证器,则LocalValidatorFactoryBean注册为全局验证器,用于@ControllerValidated @Controller方法参数。

在Java配置中,您可以自定义全局Validator实例:

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

        @Override
        public Validator getValidator(); {
                // ...
        }

}

请注意,您还可以在本地注册Validator

@Controller
public class MyController {

        @InitBinder
        protected void initBinder(WebDataBinder binder) {
                binder.addValidators(new FooValidator());
        }

}

如果您需要在某处注册LocalValidatorFactoryBean,请创建一个bean并使用@Primary标记,以避免与MVC配置中声明的冲突。

1.6.5.内容类型解析器

您可以从请求中配置Spring WebFlux如何确定@Controller所请求的媒体类型。默认情况下,只有“Accept”标头被检查,但您也可以启用基于查询参数的策略。

要自定义请求的内容类型分辨率:

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

        @Override
        public void configureContentTypeResolver(RequestedContentTypeResolverBuilder builder) {
                // ...
        }
}

1.6.6.HTTP消息编解码器

要自定义请求和响应正文的读取和写入方式:

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

        @Override
        public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
                // ...
        }
}

ServerCodecConfigurer提供了一组默认的读者和作家。您可以使用它来添加更多的读者和作者,自定义默认的,或者完全替换默认的。

对于Jackson JSON和XML,请考虑使用 Jackson2ObjectMapperBuilder 来定制Jackson的默认属性,并使用以下命令:

如果在类路径中检测到它们,它也会自动注册以下众所周知的模块:

  1. jackson-datatype-jdk7:支持Java 7类型,如java.nio.file.Path

  2. jackson-datatype-joda:支持Joda-Time类型。

  3. jackson-datatype-jsr310:支持Java 8 Date&Time API类型。

  4. jackson-datatype-jdk8:支持其他Java 8类型,如Optional

1.6.7.查看解析器

配置视图分辨率:

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
                // ...
        }
}

请注意,FreeMarker还需要配置基础视图技术:

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

        // ...

        @Bean
        public FreeMarkerConfigurer freeMarkerConfigurer() {
                FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
                configurer.setTemplateLoaderPath("classpath:/templates");
                return configurer;
        }

}

1.6.8.静态资源

此选项提供了从基于资源的位置列表中提供静态资源的便捷方式

在下面的示例中,给定以"/resources"开头的请求,相对路径用于在类路径上查找和提供相对于"/static"的静态资源。将为未来1年的资源提供资源,以确保最大限度地利用浏览器缓存和减少浏览器发出的HTTP请求。还会对Last-Modified头进行评估,如果存在,则返回304状态码。

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
                registry.addResourceHandler("/resources/**")
                        .addResourceLocations("/public", "classpath:/static/")
                        .setCachePeriod(31556926);
        }

}

资源处理程序还支持ResourceResolverResourceResolver的链 可用于创建用于处理优化资源的工具链。

基于从内容,固定应用程序版本或其他内容计算的MD5哈希可以使用VersionResourceResolver版本化的资源URL。一个ContentVersionStrategy(MD5哈希)是一个很好的选择,有一些显着的例外,如模块加载程序使用的JavaScript资源。

例如在你的Java配置中;

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
                registry.addResourceHandler("/resources/**")
                                .addResourceLocations("/public/")
                                .resourceChain(true)
                                .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
        }

}

您可以使用ResourceUrlProvider重写URL并应用完整的解析器和变压器链,例如插入版本。WebFlux配置提供了一个ResourceUrlProvider,因此可以将其注入到其他配置文件中。

与目前WebFlux中的Spring MVC不同,无法透明地重写静态资源URL,因为没有可以使用解析器和变换器的非阻塞链(例如Amazon S3上的资源)的视图技术。当仅服务本地资源时,解决方法是直接使用ResourceUrlProvider(例如通过自定义标签)并阻止0秒。

WebJars也通过WebJarsResourceResolver进行支持,并在类路径中存在"org.webjars:webjars-locator"时自动注册。解析器可以重写URL以包含该版本的jar,也可以匹配到没有版本的传入URL - 例如"/jquery/jquery.min.js""/jquery/1.2.0/jquery.min.js"

1.6.9.路径匹配

Spring WebFlux使用路径模式的解析表示,即PathPattern以及传入请求路径即RequestPath,这样就不需要指示是否解码请求路径,也不需要删除分号内容,因为PathPattern现在可以访问解码的路径段值并安全匹配。

Spring WebFlux也不支持后缀模式匹配,所以只有两个次要选项可以自定义相关的路径匹配 - 无论是否匹配尾部斜杠(默认为true)以及匹配是否区分大小写(false)。

要自定义这些选项:

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
                // ...
        }

}

1.6.10.高级配置模式

@EnableWebFlux导入DelegatingWebFluxConfiguration(1)为WebFlux应用程序提供默认Spring配置,(2)检测并委托WebFluxConfigurer来自定义该配置。

对于高级模式,删除@EnableWebFlux并直接从DelegatingWebFluxConfiguration扩展,而不是实现WebFluxConfigurer

@Configuration
public class WebConfig extends DelegatingWebFluxConfiguration {

        // ...

}

您可以在WebConfig中保留现有的方法,但现在您也可以从基类覆盖bean声明,并且您仍然可以在类路径上使用任意数量的其他WebMvcConfigurer

1.7.Web客户端

spring-webflux模块包含一个非阻塞的,反应式的客户端,用于HTTP请求与反动流反向压力。与服务器功能web框架共享 HTTP编解码器和其他基础设施

WebClient通过HTTP客户端库提供更高级别的API。默认情况下,它使用Reactor Netty,但可以使用不同的ClientHttpConnector插件。WebClient API返回Reactor FluxMono输出,并接受反应流Publisher作为输入(见 反应库)。

RestTemplate相比WebClient提供了一个更加功能和流畅的API,充分利用Java 8 lambdas。它支持同步和异步方案,包括流式传输,并带来非阻塞I / O的效率。

1.7.1.取回

retrieve()方法是获取响应体并进行解码的最简单方法:

    WebClient client = WebClient.create("http://example.org");

    Mono<Person> result = client.get()
            .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(Person.class);

您还可以从响应中获取解码的对象流:

    Flux<Quote> result = client.get()
            .uri("/quotes").accept(TEXT_EVENT_STREAM)
            .retrieve()
            .bodyToFlux(Quote.class);

默认情况下,使用4xx或5xx状态代码的响应会导致类型为WebClientResponseException的错误,但您可以自定义:

    Mono<Person> result = client.get()
            .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .onStatus(HttpStatus::is4xxServerError, response -> ...)
            .onStatus(HttpStatus::is5xxServerError, response -> ...)
            .bodyToFlux(Person.class);

1.7.2.交换

exchange()方法提供更多的控制。以下示例相当于retrieve(),但也提供对ClientResponse的访问:

    Mono<Person> result = client.get()
            .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
            .exchange()
            .flatMap(response -> response.bodyToMono(Person.class));

在这个级别你也可以创建一个完整的ResponseEntity

    Mono<ResponseEntity<Person>> result = client.get()
            .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
            .exchange()
            .flatMap(response -> response.bodyToEntity(Person.class));

请注意,与exchange()不同retrieve(),4xx和5xx响应没有自动错误信号。您必须检查状态代码并决定如何继续。

当您使用exchange()时,如果您不打算读取响应正文以关闭底层HTTP连接,则必须调用response.close()不这样做可能会导致连接池不一致或内存泄漏。

如果您使用身体因为强制关闭连接而关闭持久连接和连接池的好处,则不必调用response.close()

1.7.3.请求机构

请求体可以从对象编码:

    Mono<Person> personMono = ... ;

    Mono<Void> result = client.post()
            .uri("/persons/{id}", id)
            .contentType(MediaType.APPLICATION_JSON)
            .body(personMono, Person.class)
            .retrieve()
            .bodyToMono(Void.class);

您还可以拥有编码的对象流:

    Flux<Person> personFlux = ... ;

    Mono<Void> result = client.post()
            .uri("/persons/{id}", id)
            .contentType(MediaType.APPLICATION_STREAM_JSON)
            .body(personFlux, Person.class)
            .retrieve()
            .bodyToMono(Void.class);

或者如果您具有实际值,请使用syncBody快捷方式:

    Person person = ... ;

    Mono<Void> result = client.post()
            .uri("/persons/{id}", id)
            .contentType(MediaType.APPLICATION_JSON)
            .syncBody(person)
            .retrieve()
            .bodyToMono(Void.class);

1.7.4.生成器选项

创建WebClient的简单方法是通过静态工厂方法create()create(String),其中包含所有请求的基本URL。您还可以使用WebClient.builder()访问更多选项。

定制底层HTTP客户端:

    SslContext sslContext = ...

    ClientHttpConnector connector = new ReactorClientHttpConnector(
            builder -> builder.sslContext(sslContext));

    WebClient webClient = WebClient.builder()
            .clientConnector(connector)
            .build();

定制用于对HTTP消息进行编码和解码HTTP编解码器

    ExchangeStrategies strategies = ExchangeStrategies.builder()
            .codecs(configurer -> {
                // ...
            })
            .build();

    WebClient webClient = WebClient.builder()
            .exchangeStrategies(strategies)
            .build();

构建器可用于插入过滤器

浏览您的IDE中的WebClient.Builder以获取与URI构建相关的其他选项,默认标题(和Cookie)等。

在构建WebClient之后,您可以随时从中获取一个新的构建器,以便基于但不影响当前实例构建新的WebClient

    WebClient modifiedClient = client.mutate()
            // user builder methods...
            .build();

1.7.5.过滤器

WebClient支持拦截样式请求过滤:

    WebClient client = WebClient.builder()
        .filter((request, next) -> {

                ClientRequest filtered = ClientRequest.from(request)
                    .header("foo", "bar")
                    .build();

                return next.exchange(filtered);
        })
        .build();

ExchangeFilterFunctions提供基本认证的过滤器:

// static import of ExchangeFilterFunctions.basicAuthentication

    WebClient client = WebClient.builder()
        .filter(basicAuthentication("user", "pwd"))
        .build();

您还可以对现有的WebClient实例进行突变,而不影响原始:

    WebClient filteredClient = client.mutate()
            .filter(basicAuthentication("user", "pwd")
            .build();

1.8.反应库

Reactor是spring-webflux模块的必需依赖项,在内部用于组合逻辑和反馈流支持。一个容易记住的规则是,WebFlux API返回FluxMono - 因为这是内部使用的,并且宽松地接受任何反应流Publisher实现。

使用FluxMono有助于表示基数 - 例如是否预期单个或多个异步值。这对于API设计很重要,但在某些情况下也是至关重要的,例如编码HTTP消息时。

对于带注释的控制器,WebFlux可以透明地适应正反转的基数的反应库。这是通过来自spring-core ReactiveAdapterRegistry的帮助完成的, 它为反应和异步类型提供可插拔的支持。注册表内置了对RxJava和CompletableFuture的支持,但其他注册表可以注册。

对于功能端点,WebClient和其他功能API适用于WebFlux API的一般经验法则:

  • FluxMono作为返回值 - 使用它们构成逻辑或传递给任何Reactive Streams库(均为Publisher实现)。

  • 用于输入的活动流Publisher - 如果提供来自另一个活动库的Publisher,则只能将其视为具有未知语义(0..N)的流。如果语义是已知的 - 例如io.reactivex.Single,可以使用Mono.from(Publisher),而不是原始Publisher

例如,鉴于Publisher不是Mono,杰克逊JSON消息作者希望有多个值。如果媒体类型意味着无限流 - 例如"application/json+stream",则单独写入和刷新值; 否则,值缓存到列表中并呈现为JSON数组。