SpringWebFlux
-1(5.3.12版学习笔记2021.11.09)
核心概念
1.2 反应核心
spring-web
模块包含以下对反应式 Web 应用程序的基本支持:
- 对于服务器请求处理,有两个级别的支持。
- HttpHandler:具有非阻塞 I/O 和响应流反压力的 HTTP 请求处理的基本协定,以及 Reactor Netty,Undertow,Tomcat,Jetty 和任何 Servlet 3.1 容器的适配器。
- WebHandler API:用于请求处理的更高级别的通用 Web API,在此之上构建了具体的编程模型,例如带 Comments 的控制器和功能端点。
- 对于 Client 端,有一个基本的
ClientHttpConnector
协定,以执行具有非阻塞 I/O 和响应流反压力的 HTTP 请求,以及用于Reactor Netty和响应Jetty HtpClient的适配器。应用程序中使用的较高级别WebClient以此基本 Contract 为基础。 - 对于 Client 端和服务器,codecs用于对 HTTP 请求和响应内容进行序列化和反序列化。
1.2.1 HttpHandler
HttpHandler是具有单个方法的简单 Contract,用于处理请求和响应。它是故意最小的,它的主要也是唯一的目的是成为对不同 HTTP 服务器 API 的最小抽象。
下表描述了受支持的服务器 API:
Server name | 使用的服务器 API | 反应式流支持 |
---|---|---|
Netty | Netty API | Reactor Netty |
Undertow | Undertow API | spring-web:向响应流 bridge 过渡 |
Tomcat | Servlet 3.1 非阻塞 I/O; Tomcat API 读写 ByteBuffers 与 byte [] | spring-web:Servlet 3.1 非阻塞 I/O 到响应流 bridge |
Jetty | Servlet 3.1 非阻塞 I/O; Jetty API 编写 ByteBuffers 与 byte [] | spring-web:Servlet 3.1 非阻塞 I/O 到响应流 bridge |
Servlet 3.1 容器 | Servlet 3.1 非阻塞 I/O | spring-web:Servlet 3.1 非阻塞 I/O 到响应流 bridge |
下表描述了服务器依赖性(另请参见supported versions):
Server name | Group id | Artifact name |
---|---|---|
Reactor Netty | io.projectreactor.netty | reactor-netty |
Undertow | io.undertow | undertow-core |
Tomcat | org.apache.tomcat.embed | tomcat-embed-core |
Jetty | org.eclipse.jetty | jetty-server, jetty-servlet |
下面的代码段显示了在每个服务器 API 中使用
HttpHandler
适配器:
Reactor Netty
HttpHandler handler = ...
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create().host(host).port(port).handle(adapter).bind().block();
Undertow
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();
Jetty
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 3.1 容器
要作为 WAR 部署到任何 Servlet 3.1 容器,您可以扩展AbstractReactiveWebInitializer并将其包含在 WAR 中。该类用ServletHttpHandlerAdapter
包装HttpHandler
并将其注册为Servlet
。
1.2.2 WebHandler
API
org.springframework.web.server
包构建在HttpHandlerContracts 的基础上,以提供通用 Web API,以通过多个WebExceptionHandler,多个WebFilter和单个WebHandler组件链处理请求。可以通过简单地指向组件为auto-detected的 SpringApplicationContext
和/或通过向构建器注册组件来将链与WebHttpHandlerBuilder
放在一起。
HttpHandler
的抽象目标很简单,而WebHandler
API 的目的是提供 Web 应用程序中常用的更广泛的功能集,例如:
- 具有属性的用户会话。
- Request attributes.
- 解决了
Locale
或Principal
的请求。 - 访问已解析和缓存的表单数据。
- Multipart 数据的抽象。
- and more…
特殊 bean 类型
下表列出了WebHttpHandlerBuilder
可以在 Spring ApplicationContext 中自动检测的组件,或者可以直接向其注册的组件:
Bean name | Bean type | Count | Description |
---|---|---|---|
WebExceptionHandler | 0…N | 提供对来自WebFilter 实例链和目标WebHandler 的异常的处理。有关更多详细信息,请参见Exceptions。 | |
WebFilter | 0…N | 在其余的过滤链和目标WebHandler 之前和之后应用拦截样式逻辑。有关更多详细信息,请参见Filters。 | |
webHandler | WebHandler | 1 | 请求的处理程序。 |
webSessionManager | WebSessionManager | 0…1 | 通过ServerWebExchange 上的方法公开的WebSession 个实例的 Management 器。 DefaultWebSessionManager 默认情况下。 |
serverCodecConfigurer | ServerCodecConfigurer | 0…1 | 用于访问HttpMessageReader 实例以解析表单数据和 Multipart 数据,然后通过ServerWebExchange 上的方法公开这些数据。 ServerCodecConfigurer.create() 默认情况下。 |
localeContextResolver | LocaleContextResolver | 0…1 | LocaleContext 的解析器通过ServerWebExchange 上的方法公开。 AcceptHeaderLocaleContextResolver 默认情况下。 |
forwardedHeaderTransformer | ForwardedHeaderTransformer | 0…1 | 对于处理转发的类型 Headers,可以通过提取和删除它们或仅通过删除它们来进行。默认不使用。 |
Form Data (表单数据)
ServerWebExchange
公开了以下访问表单数据的方法:
Mono<MultiValueMap<String, String>> getFormData();
DefaultServerWebExchange
使用配置的HttpMessageReader
将表单数据(application/x-www-form-urlencoded
)解析为MultiValueMap
。缺省情况下,FormHttpMessageReader
配置为由ServerCodecConfigurer
bean 使用(请参见Web 处理程序 API)。
Multipart Data (多部分数据(文件))
ServerWebExchange
公开了以下访问 Multipart 数据的方法:
Mono<MultiValueMap<String, Part>> getMultipartData();
DefaultServerWebExchange
使用配置的HttpMessageReader<MultiValueMap<String, Part>>
将multipart/form-data
内容解析为MultiValueMap
。目前,Synchronoss NIOMultipart是唯一受支持的第三方库,也是我们知道的用于非阻塞解析 Multipart 请求的唯一库。通过ServerCodecConfigurer
bean(请参阅Web 处理程序 API)启用了它。要以流方式解析 Multipart 数据,可以改用
HttpMessageReader<Part>
返回的Flux<Part>
。例如,在带 Comments 的控制器中,使用@RequestPart
意味着按名称对单个部分进行Map
类访问,因此需要完整地解析 Multipart 数据。相反,您可以使用@RequestBody
将内容解码为Flux<Part>
而不收集为MultiValueMap
。
Forwarded Headers (转发)
当请求通过代理(例如负载平衡器)进行处理时,主机,端口和方案可能会更改,从 Client 端的角度来看,要创建指向正确的主机,端口和方案的链接是一个挑战。
RFC 7239定义
Forwarded
HTTPHeaders,代理可用来提供有关原始请求的信息。还有其他非标准 Headers,包括X-Forwarded-Host
,X-Forwarded-Port
,X-Forwarded-Proto
,X-Forwarded-Ssl
和X-Forwarded-Prefix
。
ForwardedHeaderTransformer
是一个组件,可根据转发的 Headers 修改请求的主机,端口和方案,然后删除这些 Headers。您可以将其声明为名称为forwardedHeaderTransformer
的 bean,并且它是detected并且已使用。对于转发的 Headers,存在安全方面的考虑,因为应用程序无法知道 Headers 是由代理添加的,还是由恶意 Client 端添加的。这就是为什么应配置信任边界处的代理以删除来自外部的不受信任的转发流量的原因。您也可以使用
removeOnly=true
配置ForwardedHeaderTransformer
,在这种情况下,它会删除但不使用标题。
1.2.3 Filters (过滤器)
在WebHandler API中,您可以使用
WebFilter
在其余过滤器处理链和目标WebHandler
之前和之后应用拦截样式的逻辑。使用WebFlux Config时,注册WebFilter
就像将其声明为 Spring bean 一样简单,并且(可选)通过在 bean 声明上使用@Order
或实现Ordered
来表达优先级。
CORS (跨域)
Spring WebFlux 通过控制器上的 Comments 为 CORS 配置提供了细粒度的支持。但是,当您将其与 Spring Security 结合使用时,我们建议您依赖内置的
CorsFilter
,该参数必须在 Spring Security 的过滤器链之前 Order。有关更多详细信息,请参见CORS和CORS WebFilter部分。
1.2.4 Exceptions (异常)
在WebHandler API中,可以使用
WebExceptionHandler
来处理WebFilter
实例链和目标WebHandler
链中的异常。使用WebFlux Config时,注册WebExceptionHandler
就像将其声明为 Spring bean 一样简单,并且(可选)通过在 bean 声明上使用@Order
或实现Ordered
来表达优先级。下表描述了可用的
WebExceptionHandler
实现: 该处理程序在WebFlux Config中声明。
Exception Handler | Description |
---|---|
ResponseStatusExceptionHandler | 通过将响应设置为异常的 HTTP 状态代码,提供对ResponseStatusException类型的异常的处理。 |
WebFluxResponseStatusExceptionHandler | ResponseStatusExceptionHandler extensions,也可以确定任何异常上的@ResponseStatus 注解的 HTTP 状态代码。 |
1.2.5 Codecs (编解码器)
spring-web
和spring-core
模块提供了对通过非阻塞 I/O(具有 Reactive Streams 背压)在高级对象之间来回串行化和反序列化字节内容的支持。以下介绍了此支持:
- Encoder和Decoder是底层协议,用于独立于 HTTP 编码和解码内容。
- HttpMessageReader和HttpMessageWriter是对 HTTP 消息内容进行编码和解码的协定。
Encoder
可以用EncoderHttpMessageWriter
包裹以使其适合在 Web 应用程序中使用,而Decoder
可以用DecoderHttpMessageReader
包裹。- DataBuffer提取不同的字节缓冲区表示形式(例如 Netty
ByteBuf
,java.nio.ByteBuffer
等),并且是所有编解码器都在处理的内容。有关此主题的更多信息,请参见“ Spring Core”部分中的数据缓冲区和编解码器。
spring-core
模块提供byte[]
,ByteBuffer
,DataBuffer
,Resource
和String
编码器和解码器实现。 spring-web
模块提供 Jackson JSON,Jackson Smile,JAXB2,Protocol Buffers 和其他编码器和解码器,以及用于表单数据,Multipart 内容,服务器发送的事件以及其他内容的纯 Web HTTP 消息读取器和写入器实现。
ClientCodecConfigurer
和ServerCodecConfigurer
通常用于配置和自定义要在应用程序中使用的编解码器。请参阅有关配置HTTP 消息编解码器的部分。
Jackson JSON
存在 Jackson 库时,都支持 JSON 和二进制 JSON(Smile)。
Jackson2Decoder
的工作方式如下:
- Jackson 的异步,非阻塞解析器用于将字节块流聚合到
TokenBuffer
,每个_代表一个 JSON 对象。 - 每个
TokenBuffer
都传递给 Jackson 的ObjectMapper
以创建更高级别的对象。 - 解码为单值发布者(例如
Mono
)时,有一个TokenBuffer
。 - 当解码为多值发布者(例如
Flux
)时,只要为完整格式的对象接收到足够的字节,每个TokenBuffer
就会传递给ObjectMapper
。Importing 的内容可以是 JSON 数组,如果 Content Type 为“ application/stream json”,则为line-delimited JSON。
Jackson2Encoder
的工作方式如下:
- 对于单个价值发布者(例如
Mono
),只需通过ObjectMapper
对其进行序列化即可。 - 对于具有“ application/json”的多值发布者,默认情况下使用
Flux#collectToList()
收集值,然后序列化结果集合。 - 对于具有流媒体类型(例如
application/stream+json
或application/stream+x-jackson-smile
)的多值发布者,请使用line-delimited JSON格式分别对每个值进行编码,写入和刷新。 - 对于 SSE,每个事件都调用
Jackson2Encoder
,并且刷新输出以确保传递时没有延迟。
默认情况下,
Jackson2Encoder
和Jackson2Decoder
都不支持String
类型的元素。相反,默认假设是一个字符串或一系列字符串表示要由CharSequenceEncoder
呈现的序列化 JSON 内容。如果您需要从Flux<String>
渲染 JSON 数组,请使用Flux#collectToList()
并编码Mono<List<String>>
。
Form Data
FormHttpMessageReader
和FormHttpMessageWriter
支持对“ application/x-www-form-urlencoded”内容进行解码和编码。
在经常需要从多个位置访问表单内容的服务器端,ServerWebExchange
提供了专用的getFormData()
方法,该方法通过FormHttpMessageReader
解析内容,然后缓存结果以进行重复访问。请参阅WebHandler API部分中的Form Data。
使用getFormData()
后,将无法再从请求正文中读取原始原始内容。因此,与从原始请求主体读取数据相比,应用程序应始终通过ServerWebExchange
访问缓存的表单数据。
Multipart
MultipartHttpMessageReader
和MultipartHttpMessageWriter
支持对“Multipart/表单数据”内容进行解码和编码。依次将MultipartHttpMessageReader
委派给另一个HttpMessageReader
以便实际解析为Flux<Part>
,然后将这些部分简单地收集到MultiValueMap
中。目前,Synchronoss NIOMultipart用于实际解析。
在可能需要从多个位置访问 Multipart 表单内容的服务器端,ServerWebExchange
提供了专用的getMultipartData()
方法,该方法通过MultipartHttpMessageReader
解析内容,然后缓存结果以进行重复访问。请参阅WebHandler API部分中的Multipart Data。
使用getMultipartData()
后,将无法再从请求正文中读取原始原始内容。因此,应用程序必须始终使用getMultipartData()
来重复,类似 Map 地访问 Component,否则必须依靠SynchronossPartHttpMessageReader
来一次性访问Flux<Part>
。
Limits
Decoder
和HttpMessageReader
该缓冲区一些或所有输入流的实施方式中可以与字节在内存中缓冲的最大数量的限制来配置。在某些情况下,会发生缓冲是因为输入被聚合并表示为单个对象——例如,带有@RequestBody byte[]
、 x-www-form-urlencoded
数据等的控制器方法。在拆分输入流时,流式传输也可能发生缓冲 — 例如,分隔文本、JSON 对象流等。对于那些流情况,限制适用于与流中的一个对象关联的字节数。
要配置缓冲区大小,您可以检查给定的Decoder
或HttpMessageReader
公开的maxInMemorySize
属性,如果是,Javadoc 将提供有关默认值的详细信息。在服务器端,ServerCodecConfigurer
提供设置所有编解码器的单一位置,请参阅HTTP 消息编解码器。在客户端,可以在WebClient.Builder 中更改所有编解码器的限制 。
对于多部分解析,该maxInMemorySize
属性限制了非文件部分的大小。对于文件部分,它确定将部分写入磁盘的阈值。对于写入磁盘的文件部分,还有一个附加 maxDiskUsagePerPart
属性来限制每个部分的磁盘空间量。还有一个maxParts
属性可以限制多部分请求中的部分总数。要配置所有三个WebFlux,你需要提供的预先配置的实例 MultipartHttpMessageReader
来ServerCodecConfigurer
。
Streaming
在流式传输到 HTTP 响应(例如text/event-stream
,application/stream+json
)时,定期发送数据很重要,以便尽早而不是稍后可靠地检测到断开连接的 Client 端。这样的发送可以是仅 Comments 的空 SSE 事件,也可以是有效用作心跳的任何其他“无操作”数据。
DataBuffer
DataBuffer
是 WebFlux 中字节缓冲区的表示形式。参考的 Spring Core 部分在数据缓冲区和编解码器的部分中有更多内容。要理解的关键点是,在诸如 Netty 之类的某些服务器上,字节缓冲区被池化并引用计数,并且在消耗字节缓冲区时必须将其释放以避免内存泄漏。
WebFlux 应用程序通常无需关心此类问题,除非它们直接使用或产生数据缓冲区,而不是依赖于编解码器与更高级别的对象进行转换。或者,除非他们选择创建自定义编解码器。对于这种情况,请查看数据缓冲区和编解码器中的信息,尤其是Using DataBuffer部分。
自定义编解码器
应用程序可以注册自定义编解码器以支持其他媒体类型或默认编解码器不支持的特定行为。
开发人员表达的一些配置选项在默认编解码器上强制执行。自定义编解码器可能希望有机会与这些首选项保持一致,例如强制执行缓冲限制 或记录敏感数据。
以下示例显示了如何对客户端请求执行此操作:
WebClient webClient = WebClient.builder()
.codecs(configurer -> {
CustomDecoder decoder = new CustomDecoder();
configurer.customCodecs().registerWithDefaultConfig(decoder);
})
.build();
1.2.6 Logging (日志)
Spring WebFlux 中的 DEBUG 级别日志记录旨在紧凑,最小化并且对用户友好。它侧重于一遍又一遍有用的高价值信息,而其他信息则仅在调试特定问题时才有用。
TRACE 级别的日志记录通常遵循与 DEBUG 相同的原理(例如,也不应成为 firehose),但可用于调试任何问题。此外,某些日志消息在 TRACE vs DEBUG 上可能显示不同级别的详细信息。
良好的日志记录来自使用日志的经验。
Log Id
在 WebFlux 中,单个请求可以在多个线程上执行,并且线程 ID 对于关联属于特定请求的日志消息没有用。这就是为什么 WebFlux 日志消息默认情况下带有特定于请求的 ID 的原因。
在服务器端,日志 ID 存储在
ServerWebExchange
属性(LOG_ID_ATTRIBUTE)中,而ServerWebExchange#getLogPrefix()
提供了基于该 ID 的完全格式化的前缀。在WebClient
端,日志 ID 存储在ClientRequest
属性(LOG_ID_ATTRIBUTE)中,而ClientRequest#logPrefix()
提供了完整格式的前缀。
Sensitive Data (敏感数据)
DEBUG
和TRACE
日志记录可以记录敏感信息。这就是默认情况下屏蔽表单参数和标题的原因,并且必须显式启用它们的完整日志记录。以下示例显示了如何针对服务器端请求执行此操作:
@Configuration
@EnableWebFlux
class MyConfig implements WebFluxConfigurer {
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.defaultCodecs().enableLoggingRequestDetails(true);
}
}
以下示例显示了如何针对 Client 端请求执行此操作:
Consumer<ClientCodecConfigurer> consumer = configurer ->
configurer.defaultCodecs().enableLoggingRequestDetails(true);
WebClient webClient = WebClient.builder()
.exchangeStrategies(ExchangeStrategies.builder().codecs(consumer).build())
.build();
诸如 SLF4J 和 Log4J 2 之类的日志记录库提供了避免阻塞的异步记录器。虽然这些都有自己的缺点,例如可能会丢弃无法排队等待日志记录的消息,但它们是当前用于响应式、非阻塞应用程序的最佳可用选项。
1.3 DispatcherHandler
与 Spring MVC 类似,Spring WebFlux 围绕前端控制器模式进行设计,其中中央
WebHandler
,DispatcherHandler
提供了用于请求处理的共享算法,而实际工作是由可配置的委托组件执行的。该模型非常灵活,并支持多种工作流程。
DispatcherHandler
从 Spring 配置中发现所需的委托组件。它还被设计为 Spring Bean 本身,并实现ApplicationContextAware
以访问其运行的上下文。如果以webHandler
的 bean 名称声明了DispatcherHandler
,则依次由WebHttpHandlerBuilder发现,该_组合了一个请求处理链,如WebHandler API中所述。
WebFlux 应用程序中的 Spring 配置通常包含:
DispatcherHandler
,名称为webHandler
WebFilter
和WebExceptionHandler
bean- DispatcherHandler 特殊 bean
- Others
配置已分配给WebHttpHandlerBuilder
以构建处理链,如以下示例所示:
ApplicationContext context = ...
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context).build();
生成的
HttpHandler
准备与server adapter一起使用。
特殊 bean 类
DispatcherHandler
委托特殊 bean 处理请求并呈现适当的响应。所谓“特殊 bean”,是指实现 WebFlux 框架 Contract 的 SpringManagement 的Object
实例。这些通常带有内置 Contract,但是您可以自定义它们的属性,扩展它们或替换它们。下表列出了
DispatcherHandler
. 请注意,在较低级别还检测到一些其他 bean(请参阅 Web Handler API 中的特殊 bean 类型)。
Bean type | 解释 |
---|---|
HandlerMapping | 将请求映射到处理程序。映射基于一些标准,其细节因HandlerMapping 实现而异——带注释的控制器、简单的 URL 模式映射等。主要HandlerMapping 实现RequestMappingHandlerMapping 用于带 @RequestMapping 注释的方法、RouterFunctionMapping 功能端点路由以及SimpleUrlHandlerMapping URI 路径模式和WebHandler 实例的显式注册。 |
HandlerAdapter | 帮助DispatcherHandler 调用映射到请求的处理程序,而不管处理程序实际是如何调用的。例如,调用带注释的控制器需要解析注释。a 的主要目的HandlerAdapter 是屏蔽 DispatcherHandler 这些细节。 |
HandlerResultHandler | 处理处理程序调用的结果并最终确定响应。请参阅结果处理。 |
WebFlux 配置
应用程序可以声明处理请求所需的基础结构 bean(在Web 处理程序 API和DispatcherHandler下列出)。但是,在大多数情况下,WebFlux Config是最佳起点。它声明了所需的 bean,并提供了更高级别的配置回调 API 来对其进行自定义。
Spring Boot 依靠 WebFlux 配置来配置 Spring WebFlux,并且还提供了许多额外的方便选项。
Processing
DispatcherHandler
处理请求的方式如下:
- 要求每个
HandlerMapping
查找匹配的处理程序,并使用第一个匹配项。 - 如果找到处理程序,则通过适当的
HandlerAdapter
执行该处理程序,该处理程序将执行返回的值公开为HandlerResult
。 - 通过直接写入响应或使用视图进行渲染,将
HandlerResult
赋予适当的HandlerResultHandler
以完成处理。
结果处理
通过HandlerAdapter
调用处理程序的返回值与HandlerResult
一起包装为HandlerResult
,并附加到其他上下文中,并传递给要求支持它的第一个HandlerResultHandler
。下表显示了可用的HandlerResultHandler
实现,所有实现均在WebFlux Config中声明:
结果处理程序类型 | Return Values | Default Order |
---|---|---|
ResponseEntityResultHandler | ResponseEntity ,通常来自@Controller 个实例。 | 0 |
ServerResponseResultHandler | ServerResponse ,通常来自功能端点。 | 0 |
ResponseBodyResultHandler | 处理来自@ResponseBody 个方法或@RestController 个类的返回值。 | 100 |
ViewResolutionResultHandler | CharSequence ,View,Model,Map ,Rendering或任何其他Object 被视为模型属性。 另请参见View Resolution。 `` | Integer.MAX_VALUE |
Exceptions
从HandlerAdapter
返回的HandlerResult
可以公开基于某些特定于处理程序的机制进行错误处理的函数。在以下情况下将调用此错误函数:
- 处理程序(例如
@Controller
)调用失败。 - 通过
HandlerResultHandler
处理处理程序返回值失败。
只要在从处理程序返回的反应类型产生任何数据项之前发生错误 signal,错误函数就可以更改响应(例如,更改为错误状态)。
这就是支持@Controller
类中的@ExceptionHandler
方法的方式。相比之下,Spring MVC 中对HandlerExceptionResolver
的支持基于此。这通常不重要。但是,请记住,在 WebFlux 中,不能使用@ControllerAdvice
处理在(控制层)选择处理程序之前发生的异常。
另请参见“带 Comments 的控制器”部分中的Managing Exceptions或 WebHandler API 部分中的Exceptions。
查看视图
视图分辨率使您可以使用 HTML 模板和模型渲染到浏览器,而无需将您与特定的视图技术联系在一起。在 Spring WebFlux 中,通过使用ViewResolver
实例将 String(代表逻辑视图名称)Map 到View
实例的专用HandlerResultHandler支持视图解析。 View
然后用于呈现响应。
Handling
传递给ViewResolutionResultHandler
的HandlerResult
包含处理程序的返回值和包含请求处理过程中添加的属性的模型。返回值将作为以下值之一进行处理:
String
,CharSequence
:通过已配置的ViewResolver
实现的列表解析为View
的逻辑视图名称。void
:根据请求路径选择默认视图名称,减去前斜杠和后斜杠,然后将其解析为View
。当未提供视图名称(例如,返回了模型属性)或异步返回值(例如,Mono
已完成为空)时,也会发生同样的情况。- Rendering:用于视图分辨率方案的 API。通过代码完成探索 IDE 中的选项。
Model
,Map
:要添加到请求模型的额外模型属性。- 任何其他:任何其他返回值(由BeanUtils#isSimpleProperty确定的简单类型除外)都将被视为要添加到模型的模型属性。除非存在处理程序方法
@ModelAttribute
注解,否则使用conventions从类名称派生属性名称。
该模型可以包含异步,反应式类型(例如,来自 Reactor 或 RxJava)。在渲染之前,AbstractView
将此类模型属性解析为具体值并更新模型。单值反应类型被解析为单个值或无值(如果为空),而多值反应类型(例如Flux<T>
)被收集并解析为List<T>
。
配置视图分辨率就像在 Spring 配置中添加ViewResolutionResultHandler
bean 一样简单。 WebFlux Config提供用于视图分辨率的专用配置 API。
有关与 Spring WebFlux 集成的视图技术的更多信息,请参见View Technologies。
Redirecting
视图名称中特殊的redirect:
前缀使您可以执行重定向。 UrlBasedViewResolver
(及其子类)将其识别为需要重定向的指令。视图名称的其余部分是重定向 URL。
最终效果与控制器返回RedirectView
或Rendering.redirectTo("abc").build()
的效果相同,但是现在控制器本身可以根据逻辑视图名称进行操作。诸如redirect:/some/resource
之类的视图名称是相对于当前应用程序的,而诸如redirect:http://example.com/arbitrary/path
之类的视图名称则重定向到绝对 URL。
Content Negotiation
ViewResolutionResultHandler
支持内容协商。它将请求媒体类型与每个选定的View
支持的媒体类型进行比较。使用支持请求的媒体类型的第一个View
。
为了支持 JSON 和 XML 之类的媒体类型,Spring WebFlux 提供了HttpMessageWriterView
,这是一个特殊的View
,它通过HttpMessageWriter呈现。通常,您可以通过WebFlux Configuration将它们配置为默认视图。如果默认视图与请求的媒体类型匹配,则始终会选择和使用它们。
1