构建反应式堆栈 Web 应用程序的支持

时间:2022-11-18 14:59:12

版本 6.0.0

文档的这一部分涵盖了对构建的反应式堆栈 Web 应用程序的支持 在反应式流​API 上以在非阻塞上运行 服务器,如 Netty、Undertow 和 Servlet 容器。各章节封面 Spring WebFlux​框架, 响应式Web 客户端​,支持测试​, 和反应式库​。对于 Servlet-stack Web 应用程序, 请参阅Web on Servlet Stack。

构建反应式堆栈 Web 应用程序的支持

1. 弹簧网络通量

Spring 框架中包含的原始 Web 框架 Spring Web MVC 是 专为 Servlet API 和 Servlet 容器构建。反应式堆栈 Web 框架, Spring WebFlux,后来在 5.0 版中添加。它是完全非阻塞的,支持反应流背压,并在以下服务器上运行: Netty、Undertow 和 Servlet 容器。

两个 Web 框架都镜像其源模块的名称 (spring-webmvc和spring-webflux) 并排共存 弹簧框架。每个模块都是可选的。应用程序可以使用一个或另一个模块,或者, 在某些情况下,两者兼而有之——例如,带有反应式的 Spring MVC 控制器。​​WebClient​

1.1. 概述

为什么创建Spring WebFlux?

部分答案是需要一个非阻塞的 Web 堆栈来处理并发 线程数量少,硬件资源少,规模小。Servlet 非阻塞 I/O 远离 Servlet API 的其余部分,其中合约是同步的 (,) 或阻止 (,)。这就是动机 作为跨任何非阻塞运行时的基础的新通用 API。那是 重要,因为服务器(如 Netty)在异步中建立良好, 无阻塞空间。​​Filter​​​​Servlet​​​​getParameter​​​​getPart​

答案的另一部分是函数式编程。就像添加注释一样 在Java 5中创造了机会(例如带注释的REST控制器或单元测试),该 在Java 8中添加lambda表达式为Java中的函数式API创造了机会。 这对于非阻塞应用程序和延续式 API 来说是一个福音(正如普及的那样 byandReactiveX),允许声明式 异步逻辑的组合。在编程模型级别,Java 8启用了Spring。 WebFlux 提供功能性 Web 端点以及带注释的控制器。​​CompletableFuture​

1.1.1. 定义“反应式”

我们谈到了“非阻塞”和“功能性”,但反应性是什么意思?

术语“响应式”是指围绕对更改做出反应而构建的编程模型 - 网络组件对 I/O 事件做出反应,UI 控制器对鼠标事件做出反应,等等。 从这个意义上说,非阻塞是被动的,因为我们现在处于模式,而不是被阻止 在操作完成或数据可用时对通知做出反应。

在Spring团队中,还有另一个重要的机制与“反应式”相关联。 这就是无阻塞背压。在同步命令性代码中,阻止调用 作为一种自然形式的背压,迫使呼叫者等待。在非阻塞中 代码,控制事件速率变得很重要,这样快速生产者就不会 压倒它的目的地。

Reactive Streams是一个小规范(在Java 9中也采用) 这定义了异步组件与背压之间的相互作用。 例如,数据存储库(充当发布服务器) 可以生成 HTTP 服务器(充当订阅者)的数据 然后可以写入响应。反应式流的主要目的是让 订阅者控制发布者生成数据的速度或速度。

1.1.2. 反应式 API

反应式流在互操作性方面起着重要作用。图书馆对此感兴趣 和基础设施组件,但作为应用程序 API 不太有用,因为它太 低级。应用程序需要一个更高级别、更丰富的功能API,以便 编写异步逻辑 — 类似于 Java 8API,但不仅适用于集合。 这就是响应式库所扮演的角色。​​Stream​

反应器是首选的反应库 Spring WebFlux.它提供单声道和通量API 类型 通过一组与 运算符的 ReactiveX 词汇表。 反应器是一个反应流库,因此,它的所有运算符都支持非阻塞背压。 Reactor 非常关注服务器端 Java。它是在密切合作下开发的 与春天。MonoFlux

WebFlux需要Actor作为核心依赖项,但它可以与其他反应式反应式反应器互操作 通过反应流的库。作为一般规则,WebFlux API 接受 plainas 输入,在内部将其适应 Reactor 类型,使用该类型,并返回 aor aas 输出。因此,您可以传递anyas输入,并且可以申请 对输出的操作,但您需要调整输出以用于另一个反应式库。 只要可行(例如,带注释的控制器),WebFlux 就会透明地适应使用 的 RxJava 或其他响应式库。有关更多详细信息,请参阅响应式库。​​Publisher​​​​Flux​​​​Mono​​​​Publisher​

1.1.3. 编程模型

该模块包含作为Spring WebFlux基础的响应式基础, 包括 HTTP 抽象、响应式流适配器(支持) 服务器、编解码器和核心WebHandlerAPI可与 Servlet API,但具有非阻塞合约。spring-web

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

  • 带注释的控制器:与Spring MVC一致,并基于相同的注释 从模块。Spring MVC 和 WebFlux 控制器都支持反应式 (Reactor 和 RxJava) 返回类型,因此,要区分它们并不容易。一个值得注意的 不同之处在于 WebFlux 也支持响应式参数。spring-web@RequestBody
  • 功能终端节点:基于 Lambda 的轻量级函数式编程模型。你可以想到 这是一个小型库或一组实用程序,应用程序可以使用这些库来路由和 处理请求。与带注释的控制器的最大区别在于应用程序 负责从头到尾的请求处理,而不是通过声明意图 注释和被回调。

1.1.4. 适用性

Spring MVC 还是 WebFlux?

这是一个很自然的问题,但却设置了一个不合理的二分法。实际上,两者兼而有之 共同努力,扩大可用选项的范围。两者专为 彼此的连续性和一致性,它们并排可用,并反馈 双方都受益。下图显示了两者之间的关系,它们之间的关系 有共同点,并且每个都支持独特的内容:

构建反应式堆栈 Web 应用程序的支持

我们建议您考虑以下具体要点:

  • 如果你有一个运行良好的Spring MVC应用程序,则无需更改。 命令式编程是编写、理解和调试代码的最简单方法。 您有最大的库选择,因为从历史上看,大多数库都是阻塞的。
  • 如果您已经在购买非阻塞Web堆栈,Spring WebFlux提供相同的功能。 执行模型与此领域的其他模型一样受益,并且还提供了服务器的选择 (Netty、Tomcat、Jetty、Undertow 和 Servlet 容器),多种编程模型可供选择 (带注释的控制器和功能性 Web 端点),以及反应式库的选择 (Reactor、RxJava 或其他)。
  • 如果您对与Java 8 lambda一起使用的轻量级,功能性Web框架感兴趣 或 Kotlin,您可以使用 Spring WebFlux 功能 Web 端点。那也是一个不错的选择 适用于要求不太复杂的小型应用程序或微服务,可以受益 从更高的透明度和控制。
  • 在微服务架构中,您可以将应用程序与Spring MVC混合使用。 或 Spring WebFlux 控制器或具有 Spring WebFlux 功能端点。获得支持 对于两个框架中相同的基于注释的编程模型,可以更轻松地 重用知识,同时为正确的工作选择正确的工具。
  • 评估应用程序的一种简单方法是检查其依赖项。如果您有阻塞 持久性API(JPA,JDBC)或网络API使用,Spring MVC是最佳选择 至少对于常见架构。反应器和 RxJava 在单独的线程上执行阻塞调用,但您不会进行 大多数非阻塞 Web 堆栈。
  • 如果你有一个调用远程服务的Spring MVC应用程序,请尝试反应式。 你可以返回反应式类型(Reactor,RxJava或其他) 直接来自Spring MVC控制器方法。每次调用的延迟越大,或者 呼叫之间的相互依赖性,好处越大。弹簧MVC控制器 也可以调用其他反应性组件。WebClient
  • 如果你有一个庞大的团队,请记住转向非阻塞的陡峭学习曲线, 函数式和声明式编程。无需完全切换即可启动的实用方法 是使用反应式的。除此之外,从小处着手并衡量收益。 我们预计,对于广泛的应用,这种转变是不必要的。如果你是 不确定要寻找什么好处,请首先了解非阻塞 I/O 的工作原理 (例如,单线程节点上的并发.js)及其影响。WebClient

1.1.5. 服务器

Spring WebFlux 在 Tomcat、Jetty、Servlet 容器以及 非 Servlet 运行时,如 Netty 和 Undertow。所有服务器都适应于低级通用 API,以便可以跨服务器支持更高级别的编程模型。

Spring WebFlux 没有内置的支持来启动或停止服务器。但是,它是 从 Spring 配置和WebFlux 基础架构轻松组装应用程序,并使用一些应用程序运行它 代码行。

Spring Boot 有一个 WebFlux 启动器,可以自动执行这些步骤。默认情况下,启动器使用 Netty,但通过更改您的 Maven 或 Gradle 依赖项。Spring Boot 默认为 Netty,因为它更广泛 在异步、非阻塞空间中使用,并允许客户端和服务器共享资源。

Tomcat 和 Jetty 可以与 Spring MVC 和 WebFlux 一起使用。但是请记住, 它们的使用方式非常不同。Spring MVC 依赖于 Servlet 阻塞 I/O 和 允许应用程序在需要时直接使用 Servlet API。Spring WebFlux 依赖于 Servlet 非阻塞 I/O,并在低级后面使用 Servlet API 适配器。它不会暴露在直接使用中。

对于Undertow,Spring WebFlux直接使用Undertow API,而不使用Servlet API。

1.1.6. 性能

性能具有许多特征和含义。反应性和非阻塞性 不要使应用程序运行得更快。在某些情况下,它们可以(例如,如果使用 to 并行运行远程调用)。总的来说,它需要更多的工作要做 非阻塞方式的东西,可以略微增加所需的处理时间。​​WebClient​

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

1.1.7. 并发模型

Spring MVC和Spring WebFlux都支持带注释的控制器,但有一个关键 并发模型的差异以及阻塞和线程的默认假设。

在Spring MVC(以及一般的servlet应用程序)中,假设应用程序可以 阻止当前线程(例如,用于远程调用)。出于这个原因,servlet 容器 使用大型线程池来吸收请求处理期间的潜在阻塞。

在Spring WebFlux(以及一般的非阻塞服务器)中,假设应用程序 不要阻止。因此,非阻塞服务器使用小型固定大小的线程池 (事件循环工作线程)来处理请求。

调用阻塞 API

如果您确实需要使用阻塞库怎么办?Reactor 和 RxJava 都提供了在不同线程上继续处理的运算符。这意味着有一个 轻松逃生舱口。但请记住,阻塞 API 并不适合 此并发模型。​​publishOn​

可变状态

在 Reactor 和 RxJava 中,您可以通过运算符声明逻辑。在运行时,反应式 形成管道,其中数据在不同的阶段按顺序处理。主要优势 这是它使应用程序不必保护可变状态,因为 永远不会同时调用该管道中的应用程序代码。

线程模型

您应该期望在运行Spring WebFlux的服务器上看到哪些线程?

  • 在“普通”Spring WebFlux 服务器上(例如,没有数据访问或其他可选 依赖关系),您可以期望一个线程用于服务器,其他几个线程用于请求 处理(通常与 CPU 内核数一样多)。但是,Servlet 容器 可以从更多的线程开始(例如,Tomcat 上的 10 个),以支持两个 servlet(阻塞)I/O 和 servlet 3.1(非阻塞)I/O 用法。
  • 反应式以事件循环方式操作。所以你可以看到一个小的,固定的 与此相关的处理线程数(例如,与反应器 网状连接器)。但是,如果 Reactor Netty 同时用于客户端和服务器,则两者 默认情况下共享事件循环资源。WebClientreactor-http-nio-
  • Reactor 和 RxJava 提供线程池抽象,称为调度程序,用于将处理切换到其他线程池的运算符。 调度程序具有建议特定并发策略的名称,例如“并行” (对于线程数量有限的 CPU 密集型工作)或“弹性”(适用于 I/O 密集型工作 大量线程)。如果您看到此类线程,则表示某些代码正在使用 特定的线程池策略。publishOnScheduler
  • 数据访问库和其他第三方依赖项也可以创建和使用线程 他们自己的。

配置

Spring 框架不提供对启动和停止服务器的支持。要为服务器配置线程模型, 您需要使用特定于服务器的配置 API,或者,如果您使用 Spring Boot, 检查每个服务器的 Spring 引导配置选项。您可以直接配置。 对于所有其他库,请参阅其各自的文档。​​WebClient​

1.2. 反应式核心

该模块包含以下对反应式 Web 的基础支持 应用:​​spring-web​

  • 对于服务器请求处理,有两个级别的支持。
  • HttpHandler:用于 HTTP 请求处理的基本协定 非阻塞 I/O 和反应流背压,以及用于反应器网络的适配器, Undertow,Tomcat,Jetty和任何Servlet容器。
  • WebHandler API:级别稍高的通用 Web API,用于 请求处理,在此基础上的具体编程模型,如注释 构建控制器和功能终结点。
  • 对于客户端,有一个基本的合约来执行HTTP。 具有非阻塞 I/O 和 Reactive Streams 背压的请求,以及用于 Reactor Netty、ReactiveJetty HttpClient和Apache HttpComponents 的适配器。 应用程序中使用的更高级别Web 客户端 在此基本契约的基础上构建。ClientHttpConnector
  • 对于客户端和服务器,用于序列化和 HTTP 请求和响应内容的反序列化。

1.2.1. ​​HttpHandler​

HttpHandler是一个简单的合约,具有处理请求和响应的单一方法。是的 故意最小化,其主要和唯一目的是成为最小的抽象 通过不同的 HTTP 服务器 API。

下表描述了支持的服务器 API:

服务器名称

使用的服务器接口

反应式流支持

内蒂

Netty API

反应器网

退波

暗流原料药

弹簧网:暗流到反应流桥

雄猫

Servlet 非阻塞 I/O;Tomcat API 用于读取和写入 ByteBuffers vs byte[]

spring-web:Servlet 非阻塞 I/O 到 Reactive Streams 桥接

码头

Servlet 非阻塞 I/O;Jetty API to write ByteBuffers vs byte[]

spring-web:Servlet 非阻塞 I/O 到 Reactive Streams 桥接

Servlet 容器

Servlet 非阻塞 I/O

spring-web:Servlet 非阻塞 I/O 到 Reactive Streams 桥接

下表描述了服务器依赖项(另请参阅支持的版本):

服务器名称

组标识

项目名称

反应器网

io.projectreactor.netty

反应堆内蒂

退波

io.undertow

欠拖核心

雄猫

org.apache.tomcat.embed

雄猫嵌入式核心

码头

org.eclipse.jetty

jetty-server, jetty-servlet

下面的代码片段显示了将适配器与每个服务器 API 结合使用:​​HttpHandler​

反应器网

HttpHandler handler = ...
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create().host(host).port(port).handle(adapter).bind().block();

退波

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

雄猫

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();

Servlet Container

要作为 WAR 部署到任何 Servlet 容器,您可以在 WAR 中扩展并包含AbstractReactiveWebInitializer。该类包装 anwithand 寄存器 那作为。​​HttpHandler​​​​ServletHttpHandlerAdapter​​​​Servlet​

1.2.2. API​​WebHandler​

该包基于HttpHandler合约构建 提供一个通用的 Web API,用于通过多个WebExceptionHandler、多个WebFilter 和单个WebHandler组件的链来处理请求。链条可以 通过简单地指向自动检测组件的 Spring,和/或通过注册组件来组合在一起 与建筑商。​​org.springframework.web.server​​​​WebHttpHandlerBuilder​​​​ApplicationContext​

虽然有一个简单的目标是抽象不同HTTP服务器的使用,但API旨在提供Web应用程序中常用的更广泛的功能集。 如:​​HttpHandler​​​​WebHandler​

  • 具有属性的用户会话。
  • 请求属性。
  • 已解决或请求。LocalePrincipal
  • 访问解析和缓存的表单数据。
  • 多部分数据的抽象。
  • 等等..
特殊豆类

下表列出了可以在 Spring ApplicationContext,或者可以直接注册:​​WebHttpHandlerBuilder​

豆名

豆类

计数

描述

<任何>

​WebExceptionHandler​

0..N

为实例链和目标中的异常提供处理。有关更多详细信息,请参阅​​例外​​​。​​WebFilter​​​​WebHandler​

<任何>

​WebFilter​

0..N

将拦截样式逻辑应用于过滤器链其余部分之前和之后,以及 目标。有关更多详细信息,请参阅​​筛选器​​​。​​WebHandler​

​webHandler​

​WebHandler​

1

请求的处理程序。

​webSessionManager​

​WebSessionManager​

0..1

管理器对于通过方法公开的实例 on.by 默认值。​​WebSession​​​​ServerWebExchange​​​​DefaultWebSessionManager​

​serverCodecConfigurer​

​ServerCodecConfigurer​

0..1

用于访问用于解析表单数据和多部分数据的实例,然后 通过默认 on.by 方法公开。​​HttpMessageReader​​​​ServerWebExchange​​​​ServerCodecConfigurer.create()​

​localeContextResolver​

​LocaleContextResolver​

0..1

通过方法公开的解析程序 on.by 默认值。​​LocaleContext​​​​ServerWebExchange​​​​AcceptHeaderLocaleContextResolver​

​forwardedHeaderTransformer​

​ForwardedHeaderTransformer​

0..1

用于处理转发的类型标头,方法是提取并删除它们或仅删除它们。 默认情况下不使用。

表单数据

​ServerWebExchange​​公开以下用于访问表单数据的方法:

Mono<MultiValueMap<String, String>> getFormData();

使用配置为解析表单数据 () 成 a。默认情况下,配置为由bean使用 (请参阅Web 处理程序 API)。​​DefaultServerWebExchange​​​​HttpMessageReader​​​​application/x-www-form-urlencoded​​​​MultiValueMap​​​​FormHttpMessageReader​​​​ServerCodecConfigurer​

多部分数据

网络MVC

​ServerWebExchange​​公开以下用于访问多部分数据的方法:

Mono<MultiValueMap<String, Part>> getMultipartData();

使用配置到解析内容 成一个。 默认情况下,这是没有任何第三方的 依赖。 或者,可以使用基于Synchronoss NIO多部件库的。 两者都是通过 bean 配置的 (请参阅Web 处理程序 API)。​​DefaultServerWebExchange​​​​HttpMessageReader<MultiValueMap<String, Part>>​​​​multipart/form-data​​​​MultiValueMap​​​​DefaultPartHttpMessageReader​​​​SynchronossPartHttpMessageReader​​​​ServerCodecConfigurer​

要以流式方式解析多部分数据,您可以使用 thereturn from 而不是使用,因为这意味着类似访问 按名称到各个部分,因此需要完整解析多部分数据。 相比之下,您可以使用将内容解码为没有 收集到A。​​Flux<PartEvent>​​​​PartEventHttpMessageReader​​​​@RequestPart​​​​Map​​​​@RequestBody​​​​Flux<PartEvent>​​​​MultiValueMap​

转发的标头

网络MVC

当请求通过代理(例如负载均衡器)时,主机、端口和 方案可能会改变。从客户的角度来看,这使得创建指向正确链接成为一个挑战。 主机、端口和方案。

RFC 7239定义了 HTTP 标头 代理可用于提供有关原始请求的信息。还有其他 非标准标头也是如此,包括,,,,和。​​Forwarded​​​​X-Forwarded-Host​​​​X-Forwarded-Port​​​​X-Forwarded-Proto​​​​X-Forwarded-Ssl​​​​X-Forwarded-Prefix​

​ForwardedHeaderTransformer​​是修改主机、端口和方案的组件 基于转发标头的请求,然后删除这些标头。如果您声明 它作为具有名称的 bean,它将被检测和使用。​​forwardedHeaderTransformer​

转发标头存在安全注意事项,因为应用程序无法知道 如果标头是由代理按预期添加的,或者是由恶意客户端添加的。这就是为什么 应配置信任边界的代理以删除传入的不受信任的转发流量 从外面。您还可以配置with,在这种情况下,它会删除但不使用标头。​​ForwardedHeaderTransformer​​​​removeOnly=true​

1.2.3. 过滤器

网络MVC

在WebHandlerAPI 中,您可以使用 ato 应用拦截样式 筛选器和目标的其余处理链之前和之后的逻辑。使用WebFlux 配置时,注册 ais 很简单 将其声明为春豆并(可选)通过使用 Bean 声明或通过实现。​​WebFilter​​​​WebHandler​​​​WebFilter​​​​@Order​​​​Ordered​

科尔斯

网络MVC

Spring WebFlux 通过注释为 CORS 配置提供细粒度支持 控制器。但是,当您将其与Spring Security一起使用时,我们建议您依靠内置功能,该内置功能必须在Spring Security的过滤器链之前订购。​​CorsFilter​

有关更多详细信息,请参阅CORS和CORSWebFilter部分。

1.2.4. 异常

网络MVC

在WebHandlerAPI 中,您可以使用 ato 句柄 实例链和目标中的异常。使用WebFlux 配置时,注册 ais 就像将其声明为 春豆和(可选)通过使用 bean 声明或 通过实施。​​WebExceptionHandler​​​​WebFilter​​​​WebHandler​​​​WebExceptionHandler​​​​@Order​​​​Ordered​

下表描述了可用的实现:​​WebExceptionHandler​

异常处理程序

描述

​ResponseStatusExceptionHandler​

通过将响应设置为异常的 HTTP 状态代码,提供对ResponseStatusException类型的异常的处理。

​WebFluxResponseStatusExceptionHandler​

扩展也可以确定HTTP状态 任何异常的注释代码。​​ResponseStatusExceptionHandler​​​​@ResponseStatus​

此处理程序在WebFlux 配置中声明。

1.2.5. 编解码器

网络MVC

Theand模块为序列化和 通过非阻塞 I/O 将字节内容反序列化到更高级别的对象或从更高级别的对象反序列化字节内容 反应流背压。下面介绍了此支持:spring-webpring-core

  • 编码器和解码器是低级合约 独立于 HTTP 对内容进行编码和解码。
  • HttpMessageReader 和 HttpMessageWriter是合约 对 HTTP 消息内容进行编码和解码。
  • Ancan可以包装以使其适应在网络中使用 应用,而 acan 可以包裹。EncoderEncoderHttpMessageWriterDecoderDecoderHttpMessageReader
  • 数据缓冲区抽象不同 字节缓冲区表示形式(例如 Netty 等)并且 所有编解码器的工作。请参阅中的数据缓冲区和编解码器 “Spring Core”部分了解有关此主题的更多信息。ByteBufjava.nio.ByteBuffer

该模块提供,,,,编码器和解码器实现。该模块提供杰克逊 JSON,Jackson Smile,JAXB2,Protocol Buffers和其他编码器和解码器以及 表单数据、多部分内容、 服务器发送的事件等。​​spring-core​​​​byte[]​​​​ByteBuffer​​​​DataBuffer​​​​Resource​​​​String​​​​spring-web​

​ClientCodecConfigurer​​并且通常用于配置和 自定义要在应用程序中使用的编解码器。请参阅有关配置HTTP 消息编解码器的部分。​​ServerCodecConfigurer​

杰克逊·

JSON 和二进制 JSON(微笑)是 当杰克逊图书馆存在时,两者都支持。

作品如下:​​Jackson2Decoder​

  • 杰克逊的异步、非阻塞解析器用于聚合字节块流 into 的每个都表示一个 JSON 对象。TokenBuffer
  • Eachis 传递给 Jackson 以创建一个更高级别的对象。TokenBufferObjectMapper
  • 当解码为单值发布者(例如)时,有一个。MonoTokenBuffer
  • 当解码到多值发布者(例如)时,每个都传递给 一旦为完全形成的对象接收到足够的字节。这 输入内容可以是 JSON 数组,也可以是任何行分隔的 JSON格式,例如 NDJSON, JSON 行或 JSON 文本序列。FluxTokenBufferObjectMapper

作品如下:​​Jackson2Encoder​

  • 对于单值发布者(例如),只需通过 对其进行序列化。MonoObjectMapper
  • 对于多值发布者,默认情况下收集值,然后序列化生成的集合。application/jsonFlux#collectToList()
  • 对于具有流媒体类型(如或、编码、写入和 使用行分隔的 JSON格式单独刷新每个值。其他 流媒体类型可以注册到编码器。application/x-ndjsonapplication/stream+x-jackson-smile
  • 对于 SSE,每个事件调用输出以确保 交货毫不拖延。Jackson2Encoder
表单数据

​FormHttpMessageReader​​并支持解码和编码内容。​​FormHttpMessageWriter​​​​application/x-www-form-urlencoded​

在经常需要从多个位置访问表单内容的服务器端,提供解析内容的专用方法 然后缓存结果以供重复访问。 请参阅WebHandlerAPI部分中的表单数据。​​ServerWebExchange​​​​getFormData()​​​​FormHttpMessageReader​

使用一次,无法再从 请求正文。因此,应用程序应一致地访问缓存的表单数据,而不是从原始请求正文中读取。​​getFormData()​​​​ServerWebExchange​

多部件

​MultipartHttpMessageReader​​并支持解码和 对“多部分/表单数据”内容进行编码。依次委托给 另一个用于实际解析为 aand 然后简单地 将零件收集到 A 中。 默认情况下,使用the,但这可以通过更改。 有关DefaultPartHttpMessageReader 的更多信息,请参阅 javadoc。​​MultipartHttpMessageWriter​​​​MultipartHttpMessageReader​​​​HttpMessageReader​​​​Flux<Part>​​​​MultiValueMap​​​​DefaultPartHttpMessageReader​​​​ServerCodecConfigurer​​​​DefaultPartHttpMessageReader​

在可能需要从多个部分访问多部分表单内容的服务器端 地方,提供了一个解析的专用方法 内容通过,然后缓存结果以供重复访问。 请参阅WebHandlerAPI部分中的多部分数据。​​ServerWebExchange​​​​getMultipartData()​​​​MultipartHttpMessageReader​

使用一次,无法再从 请求正文。出于这个原因,应用程序必须始终如一地使用重复的、类似地图的部件访问,或者以其他方式依赖于对部件的一次性访问。​​getMultipartData()​​​​getMultipartData()​​​​SynchronossPartHttpMessageReader​​​​Flux<Part>​

限制

​Decoder​​以及缓冲部分或全部输入的实现 可以对流配置对内存中缓冲区的最大字节数的限制。 在某些情况下,发生缓冲是因为输入被聚合并表示为单个 对象 — 例如,控制器方法,数据等。在以下情况式处理也可能发生缓冲 拆分输入流 — 例如,分隔文本、JSON 对象流,以及 等等。对于这些流式处理情况,限制适用于关联的字节数 流中有一个对象。​​HttpMessageReader​​​​@RequestBody byte[]​​​​x-www-form-urlencoded​

要配置缓冲区大小,您可以检查给定的公开属性,如果是,Javadoc 将包含有关默认值的详细信息 值。在服务器端,提供从哪里到哪里的单一位置 设置所有编解码器,请参阅HTTP 消息编解码器。在客户端,限制 所有编解码器都可以在WebClient.Builder中更改。​​Decoder​​​​HttpMessageReader​​​​maxInMemorySize​​​​ServerCodecConfigurer​

对于多部分解析属性限制 非文件部分的大小。对于文件部分,它确定部分的阈值 写入磁盘。对于写入磁盘的文件部分,还有一个附加属性来限制每个部分的磁盘空间量。还有 属性以限制分段请求中的部分总数。 要在 WebFlux 中配置所有三个实例,您需要提供一个预配置的实例。​​maxInMemorySize​​​​maxDiskUsagePerPart​​​​maxParts​​​​MultipartHttpMessageReader​​​​ServerCodecConfigurer​

网络MVC

当流式传输到 HTTP 响应(例如,,)时,定期发送数据非常重要,以便 尽早可靠地检测到断开连接的客户端。这样的发送可能是 仅评论、空的 SSE 事件或任何其他可以有效用作的“无操作”数据 心跳。​​text/event-stream​​​​application/x-ndjson​

​DataBuffer​

​DataBuffer​​是 WebFlux 中字节缓冲区的表示形式。弹簧核心部分 此参考在数据缓冲区和编解码器部分中提供了更多信息。要理解的关键点是,在某些 像Netty这样的服务器,字节缓冲区是池化和引用计数的,必须释放 消耗时以避免内存泄漏。

WebFlux 应用程序通常不需要关注此类问题,除非它们 直接使用或生成数据缓冲区,而不是依靠编解码器转换为 以及来自更高级别的对象,或者除非他们选择创建自定义编解码器。对于这样的 案例请查看数据缓冲区和编解码器中的信息, 特别是关于使用数据缓冲区的部分。

1.2.6. 日志记录

网络MVC

​DEBUG​​Spring WebFlux 中的级别日志记录被设计为紧凑、最小和 人性化。它侧重于对和有用的高价值信息位 再次与仅在调试特定问题时有用的其他。

​TRACE​​级别日志记录通常遵循与(例如 不应该是消防水带),但可用于调试任何问题。此外,一些日志 消息可能会显示不同级别的详细 ATV。​​DEBUG​​​​TRACE​​​​DEBUG​

良好的日志记录来自使用日志的经验。如果你发现任何有用的东西 没有达到既定目标,请告诉我们。

日志标识

在 WebFlux 中,单个请求可以跨多个线程运行,线程 ID 对于关联属于特定请求的日志消息没有用。这就是为什么 默认情况下,WebFlux 日志消息以特定于请求的 ID 为前缀。

在服务器端,日志 ID 存储在属性中 (LOG_ID_ATTRIBUTE), 而基于该 ID 的完全格式化的前缀可从中获取。在侧面,日志 ID 存储在属性中 (LOG_ID_ATTRIBUTE) ,而完全格式化的前缀可从。​​ServerWebExchange​​​​ServerWebExchange#getLogPrefix()​​​​WebClient​​​​ClientRequest​​​​ClientRequest#logPrefix()​

敏感数据

网络MVC

​DEBUG​​和日志记录可以记录敏感信息。这就是为什么表单参数和 默认情况下,标头是屏蔽的,您必须显式启用其完整日志记录。​​TRACE​

以下示例演示如何对服务器端请求执行此操作:

@Configuration
@EnableWebFlux
class MyConfig implements WebFluxConfigurer {

@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.defaultCodecs().enableLoggingRequestDetails(true);
}
}

以下示例演示如何对客户端请求执行此操作:

Consumer<ClientCodecConfigurer> consumer = configurer ->
configurer.defaultCodecs().enableLoggingRequestDetails(true);

WebClient webClient = WebClient.builder()
.exchangeStrategies(strategies -> strategies.codecs(consumer))
.build();
追加器

SL4J 和 Log4J 2 等日志记录库提供了异步记录器,可避免 阻塞。虽然这些有其自身的缺点,例如可能会丢弃消息 无法排队等待日志记录,它们是当前最好的可用选项 用于反应式、非阻塞应用程序。

自定义编解码器

应用程序可以注册自定义编解码器以支持其他媒体类型, 或默认编解码器不支持的特定行为。

开发人员表示的某些配置选项在默认编解码器上强制执行。 自定义编解码器可能希望有机会与这些首选项保持一致, 例如强制实施缓冲限制或记录敏感数据。

以下示例演示如何对客户端请求执行此操作:

WebClient webClient = WebClient.builder()
.codecs(configurer -> {
CustomDecoder decoder = new CustomDecoder();
configurer.customCodecs().registerWithDefaultConfig(decoder);
})
.build();

1.3. ​​DispatcherHandler​

网络MVC

Spring WebFlux,类似于Spring MVC,是围绕前端控制器模式设计的, 其中,中心,提供共享算法 请求处理,而实际工作由可配置的委托组件执行。 此模型非常灵活,支持多种工作流程。​​WebHandler​​​​DispatcherHandler​

​DispatcherHandler​​从 Spring 配置中发现它需要的委托组件。 它还被设计为一个 Spring bean 本身,并实现对它运行的上下文的访问。用豆子宣布的伊菲斯 名称,反过来,它是由WebHttpHandlerBuilder发现的, 它将请求处理链放在一起,如WebHandlerAPI 中所述。​​ApplicationContextAware​​​​DispatcherHandler​​​​webHandler​

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

  • ​DispatcherHandler​​用豆名webHandler
  • ​WebFilter​​和豆WebExceptionHandler
  • 调度程序处理程序特殊 bean
  • 别人

配置是 toto 构建处理链, 如以下示例所示:​​WebHttpHandlerBuilder​

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

结果已准备好与服务器适配器一起使用。​​HttpHandler​

1.3.1. 特殊豆类

网络MVC

委托特殊 bean 来处理请求并呈现 适当的回应。“特殊 bean”是指 Spring 管理的实例 实现 WebFlux 框架合约。这些通常带有内置合同,但是 您可以自定义其属性、扩展它们或替换它们。​​DispatcherHandler​​​​Object​

下表列出了检测到的特殊 bean。请注意, 在较低级别还检测到一些其他 bean(请参阅 Web 处理程序 API 中的特殊 Bean 类型)。​​DispatcherHandler​

豆类

解释

​HandlerMapping​

将请求映射到处理程序。映射基于一些标准,详细信息 因实现而异 — 带注释的控制器,简单 URL 模式映射等。​​HandlerMapping​

主要实现是带注释的方法,用于功能端点 路由,以及 URI 路径模式的显式注册 和实例。​​HandlerMapping​​​​RequestMappingHandlerMapping​​​​@RequestMapping​​​​RouterFunctionMapping​​​​SimpleUrlHandlerMapping​​​​WebHandler​

​HandlerAdapter​

帮助调用映射到请求的处理程序,而不考虑 如何实际调用处理程序。例如,调用带批注的控制器 需要解析批注。a.的主要目的是屏蔽这些细节。​​DispatcherHandler​​​​HandlerAdapter​​​​DispatcherHandler​

​HandlerResultHandler​

处理处理程序调用的结果并完成响应。 请参阅​​结果处理​​。

1.3.2. 网络通量配置

网络MVC

应用程序可以声明处理请求所需的基础结构 Bean(列在Web 处理程序 API和调度程序处理程序下)。 但是,在大多数情况下,WebFlux 配置是最佳起点。它声明 所需的 bean,并提供更高级别的配置回调 API 来自定义它。

1.3.3. 处理

网络MVC

​DispatcherHandler​​按如下方式处理请求:

  • 每个都要求查找匹配的处理程序,并使用第一个匹配项。HandlerMapping
  • 如果找到处理程序,则通过适当的 将执行的返回值公开为 。HandlerAdapterHandlerResult
  • 泰斯给一个适当的完成 通过直接写入响应或使用视图呈现来处理。HandlerResultHandlerResultHandler

1.3.4. 结果处理

通过 a 调用处理程序的返回值被包装 作为 A,以及一些额外的上下文,并传递给第一个声称支持它。下表显示了可用的实现,所有这些实现都在WebFlux 配置中声明:​​HandlerAdapter​​​​HandlerResult​​​​HandlerResultHandler​​​​HandlerResultHandler​

结果处理程序类型

返回值

默认订单

​ResponseEntityResultHandler​

​ResponseEntity​​​,通常来自实例。​​@Controller​

0

​ServerResponseResultHandler​

​ServerResponse​​,通常来自功能终结点。

0

​ResponseBodyResultHandler​

处理来自方法或类的返回值。​​@ResponseBody​​​​@RestController​

100

​ViewResolutionResultHandler​

​CharSequence​​​,视图​,模型​,,渲染​, 或任何其他被视为模型属性。​​Map​​​​Object​

另请参阅视图分辨率。

​Integer.MAX_VALUE​

1.3.5. 异常

网络MVC

​HandlerAdapter​​实现可以处理调用请求的内部异常 处理程序,例如控制器方法。但是,如果请求,则可能会推迟异常 处理程序返回一个异步值。

A可能会在返回时公开其异常处理机制。设置后,也会将其应用于结果的处理。​​HandlerAdapter​​​​DispatchExceptionHandler​​​​HandlerResult​​​​DispatcherHandler​

Amay也可以选择实施。该案例的旅馆将它应用于在映射处理程序之前出现的异常, 例如,在处理程序映射期间,或更早,例如在 A 中。​​HandlerAdapter​​​​DispatchExceptionHandler​​​​DispatcherHandler​​​​WebFilter​

另请参阅“带注释的控制器”部分中的异常或 WebHandler API 部分中的异常。

1.3.6. 视图分辨率

网络MVC

视图分辨率允许呈现到具有 HTML 模板的浏览器和没有 将您绑定到特定的视图技术。在Spring WebFlux中,视图分辨率为 通过专用的HandlerResultHandler提供支持,该处理程序使用实例将字符串(表示逻辑视图名称)映射到实例。然后使用Theis来呈现响应。ViewResolverViewView

处理

网络MVC

传入包含返回值 从处理程序和包含请求期间添加的属性的模型 处理。返回值按以下值之一进行处理:​​HandlerResult​​​​ViewResolutionResultHandler​

  • ​String​​,:要解析为 athrough 的逻辑视图名称 已配置实现的列表。CharSequenceViewViewResolver
  • ​void​​:根据请求路径选择默认视图名称,减去前导和 尾部斜杠,并将其解析为 a。当视图名称时也会发生同样的情况 未提供(例如,返回模型属性)或异步返回值 (例如,已完成空)。ViewMono
  • 渲染:API 查看解决方案方案。通过代码完成探索 IDE 中的选项。
  • ​Model​​,:要添加到请求的模型中的额外模型属性。Map
  • 任何其他:任何其他返回值(由BeanUtils#isSimpleProperty 确定的简单类型除外) 被视为要添加到模型中的模型属性。属性名称派生 从使用约定的类名, 除非存在处理程序方法注释。@ModelAttribute

该模型可以包含异步的响应式类型(例如,来自 Reactor 或 RxJava)。事先 渲染,将此类模型属性解析为具体值 并更新模型。单值反应类型解析为单个 值或无值(如果为空),而多值反应类型(例如,)为 收集并决心。​​AbstractView​​​​Flux<T>​​​​List<T>​

配置视图分辨率就像添加 abean 一样简单 到您的弹簧配置。WebFlux Config提供了一个 用于视图解析的专用配置 API。​​ViewResolutionResultHandler​

有关与Spring WebFlux集成的视图技术的更多信息,请参阅View Technologies。

重 定向

网络MVC

视图名称中的 specialprefix 允许您执行重定向。(和子类)将此视为一个指令 需要重定向。视图名称的其余部分是重定向 URL。​​redirect:​​​​UrlBasedViewResolver​

净效果与控制器返回 aor 相同,但现在控制器本身可以 根据逻辑视图名称进行操作。视图名称(例如相对于当前应用程序),而视图名称(例如重定向到绝对 URL)。​​RedirectView​​​​Rendering.redirectTo("abc").build()​​​​redirect:/some/resource​​​​redirect:https://example.com/arbitrary/path​

内容协商

网络MVC

​ViewResolutionResultHandler​​支持内容协商。它比较请求 媒体类型,每个选定媒体类型都支持媒体类型。使用第一个支持请求的媒体类型。​​View​​​​View​

为了支持JSON和XML等媒体类型,Spring WebFlux提供了,这是一个特殊的通过HttpMessageWriter呈现。通常,您将这些配置为默认值 通过WebFlux 配置查看。默认视图为 始终选择并使用,如果它们与请求的媒体类型匹配。​​HttpMessageWriterView​​​​View​

1.4. 带注释的控制者

网络MVC

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

下面的清单显示了一个基本示例:

@RestController
public class HelloController {

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

在前面的示例中,该方法返回要写入响应正文的 ato。​​String​

1.4.1. ​​@Controller​

网络MVC

您可以使用标准的 Spring Bean 定义来定义控制器 bean。 构造型允许自动检测,并与 Spring 常规支持保持一致 用于检测类路径中的类并自动注册 Bean 定义 对他们来说。它还充当带注释的类的构造型,指示其角色为 一个 Web 组件。​​@Controller​​​​@Component​

要启用此类豆的自动检测,您可以将组件扫描添加到 您的 Java 配置,如以下示例所示:​​@Controller​

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

// ...
}

扫描包裹。​​org.example.web​

​@RestController​​是一个组合注释,即 本身元注释与,指示其控制器 每个方法都继承类型级注释,因此写入 直接到响应正文与视图分辨率和使用 HTML 模板呈现。​​@Controller​​​​@ResponseBody​​​​@ResponseBody​

AOP 代理

网络MVC

在某些情况下,您可能需要在运行时使用 AOP 代理修饰控制器。 一个例子是,如果您选择直接在 控制器。在这种情况下,特别是对于控制器,我们建议 使用基于类的代理。对于此类注释,情况会自动出现这种情况 直接在控制器上。​​@Transactional​

如果控制器实现接口,并且需要 AOP 代理,则可能需要 显式配置基于类的代理。例如,with您可以更改为,withyou可以更改为。​​@EnableTransactionManagement​​​​@EnableTransactionManagement(proxyTargetClass = true)​​​​<tx:annotation-driven/>​​​​<tx:annotation-driven proxy-target-class="true"/>​

1.4.2. 请求映射

网络MVC

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

还有特定于 HTTP 方法的快捷方式变体:​​@RequestMapping​

  • ​@GetMapping​
  • ​@PostMapping​
  • ​@PutMapping​
  • ​@DeleteMapping​
  • ​@PatchMapping​

上述批注是提供的自定义批注 因为可以说,大多数控制器方法应该映射到特定的 HTTP 方法,而不是 使用,默认情况下,它与所有 HTTP 方法匹配。同时,在类级别仍然需要 ais 来表达共享映射。​​@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 模式

网络MVC

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

模式

描述

​?​

匹配一个字符

​"/pages/t?st.html"​​​火柴和​​"/pages/test.html"​​​​"/pages/t3st.html"​

​*​

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

​"/resources/*.png"​​​比赛​​"/resources/file.png"​

​"/projects/*/versions"​​​匹配但不匹配​​"/projects/spring/versions"​​​​"/projects/spring/boot/versions"​

​**​

匹配零个或多个路径段,直到路径结束

​"/resources/**"​​​火柴和​​"/resources/file.png"​​​​"/resources/images/file.png"​

​"/resources/**/file.png"​​​无效的 ASIS 只允许在路径的末尾。​​**​

​{name}​

匹配路径段并将其捕获为名为“name”的变量

​"/projects/{project}/versions"​​​火柴和捕获​​"/projects/spring/versions"​​​​project=spring​

​{name:[a-z]+}​

匹配名为“name”的路径变量​​"[a-z]+"​

​"/projects/{project:[a-z]+}/versions"​​​匹配但不是​​"/projects/spring/versions"​​​​"/projects/spring1/versions"​

​{*path}​

匹配零个或多个路径段,直到路径的末尾,并将其捕获为名为“path”的变量

​"/resources/{*file}"​​​火柴和捕获​​"/resources/images/file.png"​​​​file=/images/file.png​

可以使用 URI 变量访问捕获的 URI 变量,如以下示例所示:​​@PathVariable​

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

类级 URI 映射。

方法级 URI 映射。

可以在类和方法级别声明 URI 变量,如以下示例所示:

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

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

类级 URI 映射。

方法级 URI 映射。

URI 变量会自动转换为引发的相应类型或 ais。默认情况下支持简单类型(,,,等),您可以 注册对任何其他数据类型的支持。 请参见类型转换和数据绑定程序。​​TypeMismatchException​​​​int​​​​long​​​​Date​

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

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

语法声明一个 URI 变量,其正则表达式具有 语法:。例如,给定一个 URL,以下方法 提取名称、版本和文件扩展名:​​{varName:regex}​​​​{varName:regex}​​​​/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 路径模式还可以具有在启动时解析的嵌入式占位符 通过针对本地、系统、环境和 其他属性来源。例如,您可以使用它来参数化基于 一些外部配置。​​${…}​​​​PropertySourcesPlaceholderConfigurer​

Spring WebFlux不支持后缀模式匹配 - 与Spring MVC不同,Spring MVC中的 映射,例如也匹配到。对于基于 URL 的内容 协商,如果需要,我们建议使用查询参数,它更简单,更多 显式,不易受到基于 URL 路径的。​​/person​​​​/person.*​

模式比较

网络MVC

当多个模式与一个 URL 匹配时,必须比较它们以找到最佳匹配项。这是完成的 with,它查找更具体的模式。​​PathPattern.SPECIFICITY_COMPARATOR​

对于每个模式,根据 URI 变量和通配符的数量计算分数, 其中 URI 变量的分数低于通配符。总分较低的模式 赢了。如果两个模式具有相同的分数,则选择越长。

包罗万象的模式(例如,,)被排除在评分之外,并且始终 最后排序。如果两个模式都是包罗万象的,则选择越长。​​**​​​​{*varName}​

耗材介质类型

网络MVC

您可以根据请求缩小请求映射的范围, 如以下示例所示:​​Content-Type​

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

消耗属性还支持否定表达式 — 例如,表示任何 内容类型除外。​​!text/plain​​​​text/plain​

您可以在类级别声明共享属性。与大多数其他请求不同 映射属性,但是,在类级别使用时,方法级别属性 重写而不是扩展类级声明。​​consumes​​​​consumes​

可生产介质类型

网络MVC

您可以根据请求标头和列表缩小请求映射范围 控制器方法生成的内容类型,如以下示例所示:​​Accept​

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

媒体类型可以指定字符集。支持否定表达式 — 例如,表示除 以外的任何内容类型。​​!text/plain​​​​text/plain​

您可以在类级别声明共享属性。与大多数其他请求不同 映射属性,但是,在类级别使用时,方法级别属性 重写而不是扩展类级别声明。​​produces​​​​produces​

参数和标头

网络MVC

您可以根据查询参数条件缩小请求映射范围。您可以测试 存在查询参数 (),表示其不存在 (),或 特定值 ()。以下示例测试具有值的参数:​​myParam​​​​!myParam​​​​myParam=myValue​

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

检查等于。​​myParam​​​​myValue​

您还可以对请求标头条件使用相同的条件,如以下示例所示:

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

检查等于。​​myHeader​​​​myValue​

HTTP 头, 选项

网络MVC

​@GetMapping​​并支持 HTTP HEAD 透明地用于请求映射目的。控制器方法无需更改。 在服务器适配器中应用的响应包装器可确保将标头设置为写入的字节数,而无需实际写入响应。​​@RequestMapping(method=HttpMethod.GET)​​​​HttpHandler​​​​Content-Length​

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

对于没有 HTTP 方法声明的 a,标头设置为 。控制器方法应始终声明 支持的 HTTP 方法(例如,通过使用 HTTP 方法特定的变体 —,, 和其他方法)。​​@RequestMapping​​​​Allow​​​​GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS​​​​@GetMapping​​​​@PostMapping​

你可以显式地将方法映射到HTTP HEAD和HTTP OPTIONS,但是 在常见情况下不是必需的。​​@RequestMapping​

自定义注释

网络MVC

Spring WebFlux 支持使用组合注释进行请求映射。这些是本身被元注释的注释,并组成以重新声明具有更窄、更具体目的的属性子集(或全部)。​​@RequestMapping​​​​@RequestMapping​

​@GetMapping​​,,,,安代尔 组合批注的示例。之所以提供它们,是因为可以说,大多数 控制器方法应映射到特定的 HTTP 方法,而不是使用 默认情况下,它与所有 HTTP 方法匹配。如果您需要组合示例 注释,看看这些是如何声明的。​​@PostMapping​​​​@PutMapping​​​​@DeleteMapping​​​​@PatchMapping​​​​@RequestMapping​

Spring WebFlux还支持具有自定义请求匹配的自定义请求映射属性 逻辑。这是一个更高级的选项,需要子类化和覆盖方法,其中 您可以检查自定义属性并返回您自己的属性。​​RequestMappingHandlerMapping​​​​getCustomMethodCondition​​​​RequestCondition​

显式注册

网络MVC

可以通过编程方式注册处理程序方法,这些方法可用于动态 注册或高级案例,例如同一处理程序的不同实例 在不同的网址下。以下示例演示如何执行此操作:

@Configuration
public class MyConfig {

@Autowired
public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler)
throws NoSuchMethodException {

RequestMappingInfo info = RequestMappingInfo
.paths("/user/{id}").methods(RequestMethod.GET).build();

Method method = UserHandler.class.getMethod("getUser", Long.class);

mapping.registerMapping(info, handler, method);
}

}

注入目标处理程序和控制器的处理程序映射。

准备请求映射元数据。

获取处理程序方法。

添加注册。

1.4.3. 处理程序方法

网络MVC

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

方法参数

网络MVC

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

反应式类型(Reactor,RxJava或其他)是 支持需要阻塞 I/O(例如,读取请求正文)的参数 被解决。这在“说明”列中标记。不需要反应类型 在不需要阻止的参数上。

JDK 1.8 的 sis 支持作为方法参数与 具有属性的批注(例如,,, 等)和等效。​​java.util.Optional​​​​required​​​​@RequestParam​​​​@RequestHeader​​​​required=false​

控制器方法参数

描述

​ServerWebExchange​

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

​ServerHttpRequest​​​, ​​ServerHttpResponse​

访问 HTTP 请求或响应。

​WebSession​

访问会话。这不会强制启动新会话,除非属性 被添加。支持反应式类型。

​java.security.Principal​

当前经过身份验证的用户 — 如果已知,则可能是特定的实现类。 支持反应式类型。​​Principal​

​org.springframework.http.HttpMethod​

请求的 HTTP 方法。

​java.util.Locale​

当前请求区域设置,由最具体的可用 — 在 效果,配置/。​​LocaleResolver​​​​LocaleResolver​​​​LocaleContextResolver​

​java.util.TimeZone​​​ + ​​java.time.ZoneId​

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

​@PathVariable​

用于访问 URI 模板变量。请参阅URI 模式。

​@MatrixVariable​

用于访问 URI 路径段中的名称/值对。请参阅矩阵变量。

​@RequestParam​

用于访问查询参数。参数值将转换为声明的方法参数 类型。看@RequestParam。

请注意,使用 of 是可选的 — 例如,设置其属性。 请参阅此表后面的“任何其他参数”。​​@RequestParam​

​@RequestHeader​

用于访问请求标头。标头值将转换为声明的方法参数 类型。看@RequestHeader。

​@CookieValue​

用于访问饼干。Cookie 值将转换为声明的方法参数类型。 看@CookieValue。

​@RequestBody​

用于访问 HTTP 请求正文。正文内容转换为声明的方法 使用实例的参数类型。支持反应式类型。 看@RequestBody​。​​HttpMessageReader​

​HttpEntity<B>​

用于访问请求标头和正文。正文与实例一起转换。 支持反应式类型。请参阅HttpEntity​。​​HttpMessageReader​

​@RequestPart​

用于访问请求中的部件。支持反应式类型。 请参见多部分内容​和多部分数据​。​​multipart/form-data​

​java.util.Map​​​和。​​org.springframework.ui.Model​​​​org.springframework.ui.ModelMap​

用于访问 HTML 控制器中使用的模型,并以 视图渲染的一部分。

​@ModelAttribute​

用于访问模型中的现有属性(如果不存在,则实例化) 应用了数据绑定和验证。也看@ModelAttribute​ 作为模型​和数据绑定器。

请注意,使用 of 是可选的 — 例如,设置其属性。 请参阅此表后面的“任何其他参数”。​​@ModelAttribute​

​Errors​​​, ​​BindingResult​

用于访问命令对象(即参数)的验证和数据绑定中的错误。一个,或必须声明 紧跟在已验证的方法参数之后。​​@ModelAttribute​​​​Errors​​​​BindingResult​

​SessionStatus​​​+ 班级级别​​@SessionAttributes​

用于将表单处理标记为完成,这会触发会话属性的清理 通过类级注释声明。 有关更多详细信息,请参阅@SessionAttributes​。​​@SessionAttributes​

​UriComponentsBuilder​

用于准备相对于当前请求的主机、端口、方案和 上下文路径。请参阅URI 链接。

​@SessionAttribute​

用于访问任何会话属性 — 与存储在会话中的模型属性相反 作为类级声明的结果。有关更多详细信息,请参阅@SessionAttribute​。​​@SessionAttributes​

​@RequestAttribute​

用于访问请求属性。有关更多详细信息,请参阅@RequestAttribute。

任何其他参数

如果方法参数与上述任何参数不匹配,则默认情况下,它解析为 如果它是一个简单类型,由BeanUtils#isSimpleProperty​确定, 或作为,否则。​​@RequestParam​​​​@ModelAttribute​

返回值

网络MVC

下表显示了支持的控制器方法返回值。请注意,反应性 来自库(如Reactor,RxJava或其他)的类型是 通常支持所有返回值。

控制器方法返回值

描述

​@ResponseBody​

返回值通过实例进行编码并写入响应。 看​​@ResponseBody​​​。​​HttpMessageWriter​

​HttpEntity<B>​​​, ​​ResponseEntity<B>​

返回值指定完整的响应,包括 HTTP 标头,并且对正文进行编码 通过实例并写入响应。 请参阅响应实体​。​​HttpMessageWriter​

​HttpHeaders​

用于返回带有标头且没有正文的响应。

​ErrorResponse​

若要在正文中呈现包含详细信息的 RFC 7807 错误响应, 请参阅错误响应

​ProblemDetail​

若要在正文中呈现包含详细信息的 RFC 7807 错误响应, 请参阅错误响应

​String​

要解析的视图名称实例并与隐式 模型 — 通过命令对象和方法确定。处理程序 方法还可以通过声明参数以编程方式丰富模型 (如前所述​)。​​ViewResolver​​​​@ModelAttribute​​​​Model​

​View​

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

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

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

​@ModelAttribute​

要添加到模型的属性,视图名称隐式确定基于 在请求路径上。

请注意,这是可选的。请参阅后面的“任何其他返回值” 此表。​​@ModelAttribute​

​Rendering​

用于模型和视图呈现方案的 API。

​void​

具有 a、可能是异步的(例如)、返回类型(或 areturn 值)如果它还具有, 论点,或论注。同样如此 如果控制器已进行正 ETag 或时间戳检查。 待办事项:有关详细信息,请参阅控制器​。​​void​​​​Mono<Void>​​​​null​​​​ServerHttpResponse​​​​ServerWebExchange​​​​@ResponseStatus​​​​lastModified​

如果以上都不是真的,则返回类型也可以指示“无响应正文” REST 控制器或 HTML 控制器的默认视图名称选择。​​void​

​Flux<ServerSentEvent>​​​,或其他反应型​​Observable<ServerSentEvent>​

发出服务器发送的事件。当只有数据需要时,可以省略包装器 要写入(但是,必须在映射中请求或声明 通过属性)。​​ServerSentEvent​​​​text/event-stream​​​​produces​

其他返回值

如果返回值以任何其他方式仍未解析,则将其视为模型 属性,除非它是BeanUtils#isSimpleProperty 确定的简单类型, 在这种情况下,它仍然没有解决。

类型转换

网络MVC

一些表示基于字符串的请求输入的带批注的控制器方法参数(例如,,,,,和) 如果参数声明为非,则可能需要类型转换。​​@RequestParam​​​​@RequestHeader​​​​@PathVariable​​​​@MatrixVariable​​​​@CookieValue​​​​String​

对于此类情况,将根据配置的转换器自动应用类型转换。 默认情况下,支持简单类型(如,,,等)。类型转换 可以通过(参见数据绑定器)或通过注册(参见Spring 字段格式)进行自定义。​​int​​​​long​​​​Date​​​​WebDataBinder​​​​Formatters​​​​FormattingConversionService​

类型转换中的一个实际问题是处理空的 String 源值。 如果此类值成为类型转换的结果,则将其视为缺失。 对于 和其他目标类型,可能就是这种情况。如果要允许注入,请在参数注释上使用标志,或声明 参数为。​​null​​​​Long​​​​UUID​​​​null​​​​required​​​​@Nullable​

矩阵变量

网络MVC

RFC 3986讨论了 路径段。在Spring WebFlux中,我们将这些变量称为基于Tim Berners-Lee的“旧帖子”的“矩阵变量”,但它们 也可以称为 URI 路径参数。

矩阵变量可以出现在任何路径段中,每个变量用分号和 多个值以逗号分隔,例如,。倍数 也可以通过重复的变量名称来指定值,例如,。​​"/cars;color=red,green;year=2012"​​​​"color=red;color=green;color=blue"​

与Spring MVC不同,在WebFlux中,URL中矩阵变量的存在与否确实 不影响请求映射。换句话说,您不需要使用 URI 变量 以屏蔽变量内容。也就是说,如果你想从 控制器方法,需要向路径段添加一个 URI 变量,其中矩阵 变量是预期的。以下示例演示如何执行此操作:

// GET /pets/42;q=11;r=22

@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {

// petId == 42
// q == 11
}

鉴于所有路径段都可以包含矩阵变量,有时可能需要 消除矩阵变量预期在哪个路径变量中的歧义, 如以下示例所示:

// GET /owners/42;q=11/pets/21;q=22

@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable(name="q", pathVar="ownerId") int q1,
@MatrixVariable(name="q", pathVar="petId") int q2) {

// q1 == 11
// q2 == 22
}

可以定义矩阵变量也可以定义为可选并指定默认值 如以下示例所示:

// GET /pets/42

@GetMapping("/pets/{petId}")
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {

// q == 1
}

若要获取所有矩阵变量,请使用 a,如以下示例所示:​​MultiValueMap​

// GET /owners/42;q=11;r=12/pets/21;q=22;s=23

@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable MultiValueMap<String, String> matrixVars,
@MatrixVariable(pathVar="petId") MultiValueMap<String, String> petMatrixVars) {

// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
// petMatrixVars: ["q" : 22, "s" : 23]
}
​@RequestParam​

网络MVC

可以使用注释将查询参数绑定到方法参数 控制器。以下代码片段显示了用法:​​@RequestParam​

@Controller
@RequestMapping("/pets")
public class EditPetForm {

// ...

@GetMapping
public String setupForm(@RequestParam("petId") int petId, Model model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet);
return "petForm";
}

// ...
}

用。​​@RequestParam​

默认情况下,使用注释的方法参数是必需的,但 您可以通过使用 awrapper 声明参数来设置 Atoor 的必需标志,从而指定方法参数是可选的。​​@RequestParam​​​​@RequestParam​​​​false​​​​java.util.Optional​

如果目标方法参数类型不是,则会自动应用类型转换。请参见类型转换。​​String​

在 aorargument 上声明注释时,将使用所有查询参数填充映射。​​@RequestParam​​​​Map<String, String>​​​​MultiValueMap<String, String>​

请注意,使用 of 是可选的 — 例如,设置其属性。由 默认,任何简单值类型的参数(由BeanUtils#isSimpleProperty 确定) 并且未被任何其他参数解析器解析,就好像它被注释一样 跟。​​@RequestParam​​​​@RequestParam​

​@RequestHeader​

网络MVC

您可以使用注释将请求标头绑定到方法参数 控制器。​​@RequestHeader​

以下示例显示了一个带有标头的请求:

Host                    localhost:8080
Accept text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 300

下面的示例获取 theandheader 的值:​​Accept-Encoding​​​​Keep-Alive​

@GetMapping("/demo")
public void handle(
@RequestHeader("Accept-Encoding") String encoding,
@RequestHeader("Keep-Alive") long keepAlive) {
//...
}

获取标头的值。​​Accept-Encoding​

获取标头的值。​​Keep-Alive​

如果目标方法参数类型不是,则会自动应用类型转换。请参见类型转换。​​String​

当在 a、、或参数上使用注记时,将填充地图 包含所有标头值。​​@RequestHeader​​​​Map<String, String>​​​​MultiValueMap<String, String>​​​​HttpHeaders​

​@CookieValue​

网络MVC

您可以使用注释将 HTTP cookie 的值绑定到方法参数 在控制器中。​​@CookieValue​

以下示例显示了带有 cookie 的请求:

JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84

下面的代码示例演示如何获取 cookie 值:

@GetMapping("/demo")
public void handle(@CookieValue("JSESSIONID") String cookie) {
//...
}

获取饼干值。

如果目标方法参数类型不是,则会自动应用类型转换。请参见类型转换。​​String​

​@ModelAttribute​

网络MVC

您可以在方法参数上使用注释来访问属性 模型或将其实例化(如果不存在)。模型属性也覆盖了 名称与字段名称匹配的查询参数和表单字段的值。这是 称为数据绑定,它使您不必处理解析和 转换单个查询参数和表单字段。下面的示例绑定以下实例:​​@ModelAttribute​​​​Pet​

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) { }

绑定 的实例。​​Pet​

前面示例中的实例解析如下:​​Pet​

  • 如果已通过模型添加,则从模型。
  • 从 HTTP 会话到@SessionAttributes。
  • 从默认构造函数的调用。
  • 从调用具有匹配查询的参数的“主构造函数” 参数或表单字段。参数名称是通过 JavaBeansor 通过字节码中运行时保留的参数名称确定的。@ConstructorProperties

获取模型属性实例后,应用数据绑定。该类将查询参数和表单字段的名称与字段匹配 目标上的名称。应用类型转换后填充匹配字段 必要时。有关数据绑定(和验证)的详细信息,请参阅验证。有关自定义数据绑定的详细信息,请参阅数据绑定程序。​​WebExchangeDataBinder​​​​Object​

数据绑定可能会导致错误。默认情况下,ais 已引发,但是, 若要在控制器方法中检查此类错误,可以添加参数 紧挨着,如以下示例所示:​​WebExchangeBindException​​​​BindingResult​​​​@ModelAttribute​

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}

添加 a。​​BindingResult​

您可以通过添加注释或 Spring san注释在数据绑定后自动应用验证(另请参阅Bean验证和Spring 验证)。以下示例使用注释:​​jakarta.validation.Valid​​​​@Validated​​​​@Valid​

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}

使用模型属性参数。​​@Valid​

Spring WebFlux与Spring MVC不同,它支持模型中的响应式类型——例如,or。您可以声明一个参数 有或没有反应式类型包装器,它将相应地解析, 如有必要,到实际值。但是,请注意,要使用 aargument,您必须在它之前声明参数而不使用反应式 类型包装器,如前所示。或者,您可以通过 反应式类型,如以下示例所示:​​Mono<Account>​​​​io.reactivex.Single<Account>​​​​@ModelAttribute​​​​BindingResult​​​​@ModelAttribute​

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public Mono<String> processSubmit(@Valid @ModelAttribute("pet") Mono<Pet> petMono) {
return petMono
.flatMap(pet -> {
// ...
})
.onErrorResume(ex -> {
// ...
});
}

请注意,使用 of 是可选的 — 例如,设置其属性。 默认情况下,任何不是简单值类型的参数(由BeanUtils#isSimpleProperty 确定) 并且未被任何其他参数解析器解析,就好像它被注释一样 跟。​​@ModelAttribute​​​​@ModelAttribute​

​@SessionAttributes​

网络MVC

​@SessionAttributes​​用于将模型属性存储在两者之间 请求。它是一个类型级注释,用于声明由 特定控制器。这通常列出模型属性的名称或类型 应透明地存储在会话中的模型属性以供后续使用 访问请求。​​WebSession​

请考虑以下示例:

@Controller
@SessionAttributes("pet")
public class EditPetForm {
// ...
}

使用注释。​​@SessionAttributes​

在第一个请求中,当将名称为 ,的模型属性添加到模型中时, 它会自动提升并保存在。它一直留在那里,直到 另一个控制器方法使用 aMethod 参数来清除存储, 如以下示例所示:​​pet​​​​WebSession​​​​SessionStatus​

@Controller
@SessionAttributes("pet")
public class EditPetForm {

// ...

@PostMapping("/pets/{id}")
public String handle(Pet pet, BindingResult errors, SessionStatus status) {
if (errors.hasErrors()) {
// ...
}
status.setComplete();
// ...
}
}
}

使用注释。​​@SessionAttributes​

使用变量。​​SessionStatus​

​@SessionAttribute​

网络MVC

如果您需要访问全局管理的预先存在的会话属性 (即,在控制器外部 - 例如,通过过滤器)并且可能存在也可能不存在, 您可以对方法参数使用注释,如以下示例所示:​​@SessionAttribute​

爪哇岛

科特林

@GetMapping("/")
public String handle(@SessionAttribute User user) {
// ...
}

用。​​@SessionAttribute​

对于需要添加或删除会话属性的用例,请考虑注入控制器方法。​​WebSession​

用于将模型属性作为控制器的一部分临时存储在会话中 工作流,请考虑使用,如@SessionAttributes中所述。​​SessionAttributes​

​@RequestAttribute​

网络MVC

类似地,您可以使用注释来 访问之前创建的预先存在的请求属性(例如,通过 a), 如以下示例所示:​​@SessionAttribute​​​​@RequestAttribute​​​​WebFilter​

@GetMapping("/")
public String handle(@RequestAttribute Client client) {
// ...
}


多部分内容

网络MVC

如多部分数据中所述,提供对多部分的访问 内容。在控制器中处理文件上传表单(例如,从浏览器)的最佳方式 是通过数据绑定到命令对象, 如以下示例所示:​​ServerWebExchange​

class MyForm {

private String name;

private MultipartFile file;

// ...

}

@Controller
public class FileUploadController {

@PostMapping("/form")
public String handleFormUpload(MyForm form, BindingResult errors) {
// ...
}

}

用于获取元数据。​​@RequestPart​

用于获取文件。​​@RequestPart​

您还可以在 RESTful 服务中提交来自非浏览器客户端的分段请求 场景。以下示例使用文件和 JSON:

POST /someUrl
Content-Type: multipart/mixed

--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="meta-data"
Content-Type: application/json; charset=UTF-8
Content-Transfer-Encoding: 8bit

{
"name": "value"
}
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="file-data"; filename="file.properties"
Content-Type: text/xml
Content-Transfer-Encoding: 8bit
... File Data ...

您可以使用以下命令访问各个部件:​​@RequestPart​

@PostMapping("/")
public String handle(@RequestPart("meta-data") Part metadata,
@RequestPart("file-data") FilePart file) {
// ...
}

用于获取元数据。​​@RequestPart​

用于获取文件。​​@RequestPart​

要反序列化原始部件内容(例如,反序列化为 JSON — 类似于), 您可以声明一个具体目标,而不是如以下示例所示:​​@RequestBody​​​​Object​​​​Part​

@PostMapping("/")
public String handle(@RequestPart("meta-data") MetaData metadata) {
// ...
}

用于获取元数据。​​@RequestPart​

您可以与 Spring 的 sannotation 结合使用,这会导致应用标准 Bean 验证。验证 错误导致 A,导致 400 (BAD_REQUEST) 响应。 异常包含错误详细信息,也可以处理 在控制器方法中,通过使用异步包装器声明参数,然后使用 错误相关运算符:​​@RequestPart​​​​jakarta.validation.Valid​​​​@Validated​​​​WebExchangeBindException​​​​BindingResult​

@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") Mono<MetaData> metadata) {
// use one of the onError* operators...
}

用。​​@RequestBody​

要以 形式访问所有多部分数据,您可以使用, 如以下示例所示:​​MultiValueMap​​​​@RequestBody​

@PostMapping("/")
public String handle(@RequestBody Mono<MultiValueMap<String, Part>> parts) {
// ...
}

用。​​@RequestBody​

​PartEvent​

要以流式方式按顺序访问多部分数据,您可以使用 (orin Kotlin)。 多部分 HTTP 消息中的每个部分将在 至少一个包含标头和包含部件内容的缓冲区。​​@RequestBody​​​​Flux<PartEvent>​​​​Flow<PartEvent>​​​​PartEvent​

  • 表单字段将生成一个包含字段值的表单字段。FormPartEvent
  • 文件上传将生成一个或多个对象,其中包含使用的文件名 上传时。如果文件足够大,可以跨多个缓冲区拆分,则第一个缓冲区后跟后续事件。FilePartEventFilePartEvent

例如:

@PostMapping("/")
public void handle(@RequestBody Flux<PartEvent> allPartsEvents) {
allPartsEvents.windowUntil(PartEvent::isLast)
.concatMap(p -> p.switchOnFirst((signal, partEvents) -> {
if (signal.hasValue()) {
PartEvent event = signal.get();
if (event instanceof FormPartEvent formEvent) {
String value = formEvent.value();
// handle form field
}
else if (event instanceof FilePartEvent fileEvent) {
String filename = fileEvent.filename();
Flux<DataBuffer> contents = partEvents.map(PartEvent::content);
// handle file upload
}
else {
return Mono.error(new RuntimeException("Unexpected event: " + event));
}
}
else {
return partEvents; // either complete or error signal
}
}));
}

用。​​@RequestBody​

特定部分的决赛将设置为,并且可以 后跟属于后续部分的其他事件。 这使得该属性适合作为运算符的谓词, 将所有部件中的事件拆分到每个部件属于单个部件的窗口中。​​PartEvent​​​​isLast()​​​​true​​​​isLast​​​​Flux::windowUntil​

操作员允许您查看您是否正在处理表单字段或 文件上传。​​Flux::switchOnFirst​

处理表单域。

处理文件上传。

正文内容必须完全消耗、中继或释放,以避免内存泄漏。

接收的部件事件也可以通过使用中继到另一个服务。 请参阅多部分数据。​​WebClient​

​@RequestBody​

网络MVC

您可以使用注释来读取请求正文,并通过HttpMessageReader 将其反序列化为请求正文。 以下示例使用参数:​​@RequestBody​​​​Object​​​​@RequestBody​

@PostMapping("/accounts")
public void handle(@RequestBody Account account) {
// ...
}

与Spring MVC不同,在WebFlux中,method参数支持响应式类型 以及完全无阻塞的读取和(客户端到服务器)流式传输。​​@RequestBody​

@PostMapping("/accounts")
public void handle(@RequestBody Mono<Account> account) {
// ...
}

您可以使用WebFlux 配置的HTTP 消息编解码器选项来 配置或自定义消息阅读器。

您可以与 Spring 的 sannotation 结合使用,这会导致应用标准 Bean 验证。验证 错误会导致 A,从而导致 400 (BAD_REQUEST) 响应。 该异常包含错误详细信息,可以在 控制器方法,方法是使用异步包装器声明参数,然后使用 error 相关运营商:​​@RequestBody​​​​jakarta.validation.Valid​​​​@Validated​​​​WebExchangeBindException​​​​BindingResult​

@PostMapping("/accounts")
public void handle(@Valid @RequestBody Mono<Account> account) {
// use one of the onError* operators...
}
​HttpEntity​

网络MVC

​HttpEntity​​或多或少与使用相同@RequestBody但基于 公开请求标头和正文的容器对象。以下示例使用:​​HttpEntity​

@PostMapping("/accounts")
public void handle(HttpEntity<Account> entity) {
// ...
}
​@ResponseBody​

网络MVC

可以在方法上使用注释来序列化返回 通过HttpMessageWriter 到响应正文。以下 示例显示了如何执行此操作:​​@ResponseBody​

@GetMapping("/accounts/{id}")
@ResponseBody
public Account handle() {
// ...
}

​@ResponseBody​​在类级别也受支持,在这种情况下,它由 所有控制器方法。这就是效果,仅此而已 而不是标记为 withand 的元注释。​​@RestController​​​​@Controller​​​​@ResponseBody​

​@ResponseBody​​支持响应式类型,这意味着你可以返回 Reactor 或 RxJava 类型,并将它们生成的异步值呈现到响应中。 有关其他详细信息,请参阅流式理和JSON 呈现。

可以将方法与 JSON 序列化视图结合使用。 有关详细信息,请参阅Jackson JSON。@ResponseBody

您可以使用WebFlux 配置的HTTP 消息编解码器选项来 配置或自定义消息写入。

​ResponseEntity​

网络MVC

​ResponseEntity​​就像@ResponseBody但带有状态和标题。例如:

@GetMapping("/something")
public ResponseEntity<String> handle() {
String body = ... ;
String etag = ... ;
return ResponseEntity.ok().eTag(etag).body(body);
}

WebFlux 支持使用单值反应式类型来 异步生成,和/或单值和多值反应类型 为身体。这允许各种异步响应,如下所示:​​ResponseEntity​​​​ResponseEntity​

  • ​ResponseEntity<Mono<T>>​​或者做出响应状态和 标头在以后异步提供正文时立即已知。 使用如果主体由 0..1 个值组成,或者它可以生成多个值。ResponseEntity<Flux<T>>MonoFlux
  • ​Mono<ResponseEntity<T>>​​提供所有三个 — 响应状态、标头和正文, 在以后的点异步。这允许响应状态和标头变化 取决于异步请求处理的结果。
  • ​Mono<ResponseEntity<Mono<T>>>​​或者又一个 可能,尽管不太常见的替代方案。它们提供响应状态和标头 首先异步,然后是响应正文,也是异步的,其次。Mono<ResponseEntity<Flux<T>>>
杰克逊·

Spring 提供对 Jackson JSON 库的支持。

JSON 视图

网络MVC

Spring WebFlux 为Jackson 的序列化视图提供了内置支持, 这允许仅呈现 AN 中所有字段的子集。若要使用 withorcontroller 方法,可以使用 Jackson sannotation 激活序列化视图类,如以下示例所示:​​Object​​​​@ResponseBody​​​​ResponseEntity​​​​@JsonView​

@RestController
public class UserController {

@GetMapping("/user")
@JsonView(User.WithoutPasswordView.class)
public User getUser() {
return new User("eric", "7!jd#h23");
}
}

public class User {

public interface WithoutPasswordView {};
public interface WithPasswordView extends WithoutPasswordView {};

private String username;
private String password;

public User() {
}

public User(String username, String password) {
this.username = username;
this.password = password;
}

@JsonView(WithoutPasswordView.class)
public String getUsername() {
return this.username;
}

@JsonView(WithPasswordView.class)
public String getPassword() {
return this.password;
}
}

1.4.4. ​​Model​

网络MVC

您可以使用注释:​​@ModelAttribute​

  • 在方法参数方法上 从模型创建或访问对象,并通过 将其绑定到请求。@RequestMappingWebDataBinder
  • 作为方法级注释类,帮助 在 Any方法调用之前初始化模型。@Controller@ControllerAdvice@RequestMapping
  • 在方法上将其返回值标记为模型属性。@RequestMapping

本节讨论方法或前面列表中的第二项。 控制器可以有任意数量的方法。所有这些方法都是 在同一控制器中调用 before方法。Amethod也可以通过以下方式在控制器之间共享。有关更多详细信息,请参阅控制器建议部分。​​@ModelAttribute​​​​@ModelAttribute​​​​@RequestMapping​​​​@ModelAttribute​​​​@ControllerAdvice​

​@ModelAttribute​​方法具有灵活的方法签名。他们支持许多相同的 参数作为方法(除了它自己和任何东西 与请求正文相关)。​​@RequestMapping​​​​@ModelAttribute​

以下示例使用 amethod:​​@ModelAttribute​

@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {
model.addAttribute(accountRepository.findAccount(number));
// add more ...
}

以下示例仅添加一个属性:

@ModelAttribute
public Account addAccount(@RequestParam String number) {
return accountRepository.findAccount(number);
}

Spring WebFlux与Spring MVC不同,在模型中明确支持响应式类型 (例如,或)。这种异步模型 属性可以透明地解析(并更新模型)为其实际值 在调用时,如果参数是 声明时没有包装器,如以下示例所示:​​Mono<Account>​​​​io.reactivex.Single<Account>​​​​@RequestMapping​​​​@ModelAttribute​

爪哇岛

科特林

@ModelAttribute
public void addAccount(@RequestParam String number) {
Mono<Account> accountMono = accountRepository.findAccount(number);
model.addAttribute("account", accountMono);
}

@PostMapping("/accounts")
public String handle(@ModelAttribute Account account, BindingResult errors) {
// ...
}

此外,任何具有反应式类型包装器的模型属性都将解析为其 实际值(以及模型更新)就在视图渲染之前。

您还可以将方法用作方法级注释,在这种情况下,该方法的返回值被解释为 模型属性。这通常不是必需的,因为它是 HTML 中的默认行为 控制器,除非返回值为 a,否则将被解释 作为视图名称,还可以帮助自定义模型属性名称, 如以下示例所示:​​@ModelAttribute​​​​@RequestMapping​​​​@RequestMapping​​​​String​​​​@ModelAttribute​

@GetMapping("/accounts/{id}")
@ModelAttribute("myAccount")
public Account handle() {
// ...
return account;
}

1.4.5. ​​DataBinder​

网络MVC

​@Controller​​orclasses 可以有方法, 初始化实例。反过来,这些习惯于:​​@ControllerAdvice​​​​@InitBinder​​​​WebDataBinder​

  • 将请求参数(即表单数据或查询)绑定到模型对象。
  • 基于转换的请求值(例如请求参数、路径变量、 标头、Cookie 等)到控制器方法参数的目标类型。String
  • 呈现 HTML 表单时将模型对象值的格式设置为值。String

​@InitBinder​​方法可以注册特定于控制器的函数 弹簧组件。此外,您可以使用WebFlux Java 配置在全局共享中注册和键入。​​java.beans.PropertyEditor​​​​Converter​​​​Formatter​​​​Converter​​​​Formatter​​​​FormattingConversionService​

​@InitBinder​​方法支持许多与方法相同的参数 执行,除了 for(命令对象)参数。通常,它们被声明 带有参数,用于注册和返回值。 以下示例使用注释:​​@RequestMapping​​​​@ModelAttribute​​​​WebDataBinder​​​​void​​​​@InitBinder​

@Controller
public class FormController {

@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}

// ...
}

使用注释。​​@InitBinder​

或者,当通过共享使用基于 a 的设置时,您可以重复使用相同的方法并注册 特定于控制器的实例,如以下示例所示:​​Formatter​​​​FormattingConversionService​​​​Formatter​

@Controller
public class FormController {

@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
}

// ...
}

添加自定义格式化程序(在本例中为 a)。​​DateFormatter​

模型设计

网络MVC

在Web应用程序的上下文中,数据绑定涉及HTTP请求的绑定 模型对象中属性的参数(即表单数据或查询参数),以及 其嵌套对象。

只有遵循JavaBeans 命名约定的属性才会公开用于数据绑定 — 例如,和 aproperty 的方法。​​public​​​​public String getFirstName()​​​​public void setFirstName(String)​​​​firstName​

默认情况下,Spring 允许绑定到模型对象图中的所有公共属性。 这意味着您需要仔细考虑模型具有哪些公共属性,因为 客户端可以针对任何公共属性路径,甚至是一些预期不明确的公共属性路径 针对给定用例。

例如,给定一个 HTTP 表单数据端点,恶意客户端可以提供 存在于模型对象图中但不是 HTML 表单一部分的属性 显示在浏览器中。这可能会导致在模型对象和任何 的嵌套对象,预计不会更新。

建议的方法是使用仅公开的专用模型对象 与表单提交相关的属性。例如,在用于更改的窗体上 用户的电子邮件地址,模型对象应声明一组最小的属性,例如 如下。​​ChangeEmailForm​

public class ChangeEmailForm {

private String oldEmailAddress;
private String newEmailAddress;

public void setOldEmailAddress(String oldEmailAddress) {
this.oldEmailAddress = oldEmailAddress;
}

public String getOldEmailAddress() {
return this.oldEmailAddress;
}

public void setNewEmailAddress(String newEmailAddress) {
this.newEmailAddress = newEmailAddress;
}

public String getNewEmailAddress() {
return this.newEmailAddress;
}

}

如果不能或不想对每个数据使用专用模型对象 绑定用例中,您必须限制数据绑定允许的属性。 理想情况下,您可以通过方法注册允许的字段模式来实现此目的。​​setAllowedFields()​​​​WebDataBinder​

例如,要在应用程序中注册允许的字段模式,可以在主组件中实现方法,如下所示:​​@InitBinder​​​​@Controller​​​​@ControllerAdvice​

@Controller
public class ChangeEmailController {

@InitBinder
void initBinder(WebDataBinder binder) {
binder.setAllowedFields("oldEmailAddress", "newEmailAddress");
}

// @RequestMapping methods, etc.

}

除了注册允许的模式外,还可以注册不允许的模式 字段模式通过方法 in 及其子类。 但请注意,“允许列表”比“拒绝列表”更安全。因此,应该受到青睐。​​setDisallowedFields()​​​​DataBinder​​​​setAllowedFields()​​​​setDisallowedFields()​

请注意,与允许的字段模式匹配区分大小写;而, 匹配 针对不允许的字段模式不区分大小写。此外,与 不允许的模式将不被接受,即使它也恰好与 允许列表。

1.4.6. 异常

网络MVC

​@Controller​​并且@ControllerAdvice类可以具有处理控制器方法异常的方法。以下 示例包括这样的处理程序方法:​​@ExceptionHandler​

@Controller
public class SimpleController {

// ...

@ExceptionHandler
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}

声明一个。​​@ExceptionHandler​

异常可以与正在传播的*异常(即引发的直接异常)匹配,也可以与*包装器中的直接原因匹配 异常(例如,在 AN 内换行)。​​IOException​​​​IOException​​​​IllegalStateException​

对于匹配的异常类型,最好将目标异常声明为方法参数, 如前面的示例所示。或者,注释声明可以缩小 要匹配的异常类型。我们通常建议在 参数签名,并声明您的主根异常映射,并按相应的顺序优先。 有关详细信息,请参阅 MVC 部分。​​@ControllerAdvice​

Spring WebFlux 中的 forways 支持由 formethods 提供。有关更多详细信息,请参阅DispatcherHandler​。​​@ExceptionHandler​​​​HandlerAdapter​​​​@RequestMapping​

方法参数

网络MVC

​@ExceptionHandler​​方法支持与方法相同的方法参数,只是请求正文可能已被使用。​​@RequestMapping​

返回值

网络MVC

​@ExceptionHandler​​方法支持与方法相同的返回值。​​@RequestMapping​

1.4.7. 控制器建议

网络MVC

通常,、 和 方法适用 在声明它们的类(或类层次结构)中。如果你 希望这些方法更全局地应用(跨控制器),您可以在 类注释与 withor。​​@ExceptionHandler​​​​@InitBinder​​​​@ModelAttribute​​​​@Controller​​​​@ControllerAdvice​​​​@RestControllerAdvice​

​@ControllerAdvice​​被注释,这意味着这样的类可以是 通过组件扫描注册为 Spring bean。是带有注释的复合注释 使用 bothand,这实质上意味着方法通过消息转换呈现到响应体 (相对于视图分辨率或模板呈现)。​​@Component​​​​@RestControllerAdvice​​​​@ControllerAdvice​​​​@ResponseBody​​​​@ExceptionHandler​

在启动时,基础结构类forand方法检测注释的Spring bean,然后应用它们的 运行时的方法。全局方法(来自 a)是 在本地之后应用(从)。相比之下,全局方法先于局部方法应用。​​@RequestMapping​​​​@ExceptionHandler​​​​@ControllerAdvice​​​​@ExceptionHandler​​​​@ControllerAdvice​​​​@Controller​​​​@ModelAttribute​​​​@InitBinder​

默认情况下,方法适用于每个请求(即所有控制器), 但是,您可以通过在 注释,如以下示例所示:​​@ControllerAdvice​

// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}

// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}

// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}

前面示例中的选择器在运行时进行评估,可能会产生负面影响 如果广泛使用,则具有性能。有关更多详细信息,请参阅@ControllerAdvicejavadoc。