此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.0! |
响应式核心
这spring-web
module 包含对反应式 Web 的以下基本支持
应用:
-
对于服务器请求处理,有两个级别的支持。
-
HttpHandler:HTTP 请求处理的基本协定 非阻塞 I/O 和反应流背压,以及用于 Reactor Netty 的适配器, Undertow、Tomcat、Jetty 和任何 Servlet 容器。
-
WebHandler
应用程序接口:稍高级的通用 Web API 请求处理,除此之外,还有具体的编程模型,如 Annotated 构建控制器和功能端点。
-
-
对于客户端,有一个基本的
ClientHttpConnector
contract 来执行 HTTP 具有非阻塞 I/O 和反应流背压的请求,以及用于 Reactor Netty、反应式 Jetty HttpClient 和 Apache HttpComponents 的适配器。 应用程序中使用的更高级别的 WebClient 建立在这个基本契约之上。 -
对于客户端和服务器,用于序列化的编解码器和 HTTP 请求和响应内容的反序列化。
HttpHandler
HttpHandler 是一个简单的协定,具有处理请求和响应的单一方法。是的 有意最小化,其主要且唯一目的是成为最小抽象 通过不同的 HTTP 服务器 API 进行。
下表描述了支持的服务器 API:
服务器名称 | 使用的服务器 API | Reactive Streams 支持 |
---|---|---|
网 |
Netty API |
|
Undertow |
Undertow API |
spring-web: Undertow 到 Reactive Streams 桥 |
Tomcat |
Servlet 非阻塞 I/O;Tomcat API 读取和写入 ByteBuffers 与 byte[] |
spring-web:Servlet 非阻塞 I/O 到 Reactive Streams 桥 |
Jetty |
Servlet 非阻塞 I/O;Jetty API 写入 ByteBuffers 与 byte[] |
spring-web:Servlet 非阻塞 I/O 到 Reactive Streams 桥 |
Servlet 容器 |
Servlet 非阻塞 I/O |
spring-web:Servlet 非阻塞 I/O 到 Reactive Streams 桥 |
下表描述了服务器依赖项(另请参阅支持的版本):
服务器名称 | 组 ID | 项目名称 |
---|---|---|
Reactor Netty |
io.projectreactor.netty |
反应器-NETTY |
Undertow |
io.undertow |
undertow-core |
Tomcat |
org.apache.tomcat.embed |
tomcat-embed-core |
Jetty |
org.eclipse.jetty 网站 |
jetty 服务器、jetty-servlet |
下面的代码片段显示了如何使用HttpHandler
adapters 的 API 中:
Reactor Netty
-
Java
-
Kotlin
HttpHandler handler = ...
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create().host(host).port(port).handle(adapter).bindNow();
val handler: HttpHandler = ...
val adapter = ReactorHttpHandlerAdapter(handler)
HttpServer.create().host(host).port(port).handle(adapter).bindNow()
Undertow
-
Java
-
Kotlin
HttpHandler handler = ...
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
server.start();
val handler: HttpHandler = ...
val adapter = UndertowHttpHandlerAdapter(handler)
val server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build()
server.start()
Tomcat
-
Java
-
Kotlin
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();
val handler: HttpHandler = ...
val servlet = TomcatHttpHandlerAdapter(handler)
val server = Tomcat()
val base = File(System.getProperty("java.io.tmpdir"))
val rootContext = server.addContext("", base.absolutePath)
Tomcat.addServlet(rootContext, "main", servlet)
rootContext.addServletMappingDecoded("/", "main")
server.host = host
server.setPort(port)
server.start()
Jetty
-
Java
-
Kotlin
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();
val handler: HttpHandler = ...
val servlet = JettyHttpHandlerAdapter(handler)
val server = Server()
val contextHandler = ServletContextHandler(server, "")
contextHandler.addServlet(ServletHolder(servlet), "/")
contextHandler.start();
val connector = ServerConnector(server)
connector.host = host
connector.port = port
server.addConnector(connector)
server.start()
Servlet 容器
要作为 WAR 部署到任何 Servlet 容器,您可以扩展并包含AbstractReactiveWebInitializer
在战争中。该类将HttpHandler
跟ServletHttpHandlerAdapter
和寄存器
那作为一个Servlet
.
WebHandler
应用程序接口
这org.springframework.web.server
软件包构建在HttpHandler
合同
提供通用的 Web API,用于通过多个WebExceptionHandler
倍数WebFilter
和单个WebHandler
元件。链条可以
与WebHttpHandlerBuilder
通过简单地指向 SpringApplicationContext
自动检测组件,和/或通过注册组件
与构建器。
而HttpHandler
有一个简单的目标,即抽象出不同 HTTP 服务器的使用,WebHandler
API 旨在提供 Web 应用程序中常用的更广泛的功能集
如:
-
具有属性的用户会话。
-
请求属性。
-
解决
Locale
或Principal
对于请求。 -
访问已解析和缓存的表单数据。
-
多部分数据的抽象。
-
和更多..
特殊 bean 类型
下表列出了WebHttpHandlerBuilder
可以在
Spring ApplicationContext,或者可以直接使用它注册:
Bean 名称 | Bean 类型 | 计数 | 描述 |
---|---|---|---|
<任意> |
|
0..N |
为来自链的异常提供处理 |
<任意> |
|
0..N |
将拦截样式逻辑应用于过滤器链的其余部分之前和之后,并且
目标 |
|
|
1 |
请求的处理程序。 |
|
|
0..1 |
的经理 |
|
|
0..1 |
要访问 |
|
|
0..1 |
的解析程序 |
|
|
0..1 |
用于处理转发类型的标头,可以通过提取和删除它们,或者仅删除它们。 默认情况下不使用。 |
表单数据
ServerWebExchange
公开以下用于访问表单数据的方法:
-
Java
-
Kotlin
Mono<MultiValueMap<String, String>> getFormData();
suspend fun getFormData(): MultiValueMap<String, String>
这DefaultServerWebExchange
使用配置的HttpMessageReader
解析表单数据
(application/x-www-form-urlencoded
) 转换为MultiValueMap
.默认情况下,FormHttpMessageReader
配置为供ServerCodecConfigurer
豆
(请参阅 Web 处理程序 API)。
多部分数据
ServerWebExchange
公开以下用于访问分段数据的方法:
-
Java
-
Kotlin
Mono<MultiValueMap<String, Part>> getMultipartData();
suspend fun getMultipartData(): MultiValueMap<String, Part>
这DefaultServerWebExchange
使用配置的HttpMessageReader<MultiValueMap<String, Part>>
解析multipart/form-data
,multipart/mixed
和multipart/related
content 转换为MultiValueMap
.
默认情况下,这是DefaultPartHttpMessageReader
,它没有任何第三方
依赖。
或者,SynchronossPartHttpMessageReader
,它基于 Synchronoss NIO Multipart 库。
两者都是通过ServerCodecConfigurer
豆
(请参阅 Web 处理程序 API)。
要以流式处理方式解析多部分数据,您可以使用Flux<PartEvent>
从PartEventHttpMessageReader
而不是使用@RequestPart
,正如这意味着Map
类访问
按名称分配给各个部分,因此需要完整解析多个部分数据。
相比之下,您可以使用@RequestBody
将内容解码为Flux<PartEvent>
没有
collecting 到MultiValueMap
.
请求头转发
当请求通过负载均衡器等代理时,主机、端口和 方案可能会更改,这使得创建指向正确 Host、Port 和 Scheme。
RFC 7239 定义了Forwarded
HTTP 标头
代理可用于提供有关原始请求的信息。
非标准标头
还有其他非标准标头,包括X-Forwarded-Host
,X-Forwarded-Port
,X-Forwarded-Proto
,X-Forwarded-Ssl
和X-Forwarded-Prefix
.
X-转发主机
虽然不是标准的,X-Forwarded-Host: <host>
是事实上的标准标头,用于将原始主机传送到
downstream server 的例如,如果example.com/resource
将发送到
一个代理,它将请求转发到localhost:8080/resource
,则X-Forwarded-Host: example.com
可以发送通知服务器原始主机是example.com
.
X 转发端口
虽然不是标准的,X-Forwarded-Port: <port>
是事实上的标准标头,用于
将原始端口传送到下游服务器。例如,如果example.com/resource
将发送到代理,该代理将请求转发到localhost:8080/resource
,则X-Forwarded-Port: 443
可以发送
通知服务器原始端口为443
.
X 转发原型
虽然不是标准的,X-Forwarded-Proto: (https|http)
是用于传达原始协议(例如 HTTPS / HTTPS)的事实标准标头
到下游服务器。例如,如果example.com/resource
将发送到
一个代理,它将请求转发到localhost:8080/resource
,则X-Forwarded-Proto: https
可以发送通知服务器原来的协议是https
.
X-转发 SSL
虽然不是标准的,X-Forwarded-Ssl: (on|off)
是事实上的标准标头,用于传达
原始协议(例如 HTTPS / HTTPS)到下游服务器。例如,如果example.com/resource
将发送到代理,该代理将请求转发到localhost:8080/resource
,则X-Forwarded-Ssl: on
通知服务器
最初的协议是https
.
X 转发前缀
虽然不是标准的,X-Forwarded-Prefix: <prefix>
是事实上的标准标头,用于将原始 URL 路径前缀传达给
downstream server 的
用途X-Forwarded-Prefix
可能因部署方案而异,并且需要灵活地
允许替换、删除或预置目标服务器的路径前缀。
场景 1:覆盖路径前缀
https://example.com/api/{path} -> http://localhost:8080/app1/{path}
前缀是 capture 组之前的路径的开头{path}
.对于代理,
前缀为/api
而对于服务器,前缀为/app1
.在这种情况下,代理
可以发送X-Forwarded-Prefix: /api
具有原始前缀/api
覆盖
服务器前缀/app1
.
方案 2:删除路径前缀
有时,应用程序可能希望删除前缀。例如,考虑 以下代理到服务器的映射:
https://app1.example.com/{path} -> http://localhost:8080/app1/{path} https://app2.example.com/{path} -> http://localhost:8080/app2/{path}
代理没有前缀,而应用程序app1
和app2
具有路径前缀/app1
和/app2
分别。代理可以发送X-Forwarded-Prefix:
自
让空前缀覆盖服务器前缀/app1
和/app2
.
此部署方案的一个常见情况是许可证按 production application server 部署多个应用程序,并且最好为每个 server 来降低费用。另一个原因是在同一台服务器上运行更多应用程序 order 共享服务器运行所需的资源。 在这些情况下,应用程序需要一个非空的上下文根,因为有多个 应用程序。但是,这在 公共 API,应用程序可以在其中使用不同的子域,从而提供优势 如:
|
方案 3:插入路径前缀
在其他情况下,可能需要在前面加上前缀。例如,考虑 以下代理到服务器的映射:
https://example.com/api/app1/{path} -> http://localhost:8080/app1/{path}
在这种情况下,代理的前缀为/api/app1
服务器具有前缀/app1
.代理可以发送X-Forwarded-Prefix: /api/app1
具有原始前缀/api/app1
覆盖服务器前缀/app1
.
转发标头转换器
ForwardedHeaderTransformer
是一个组件,用于修改
请求,然后删除这些标头。如果您声明
it 作为名为forwardedHeaderTransformer
,它将被检测并使用。
在 5.1 中ForwardedHeaderFilter 已弃用并被ForwardedHeaderTransformer 因此,请求头转发可以在
Exchange 已创建。如果仍然配置了过滤器,则会将其从
filters 和ForwardedHeaderTransformer 。 |
过滤器
在WebHandler
应用程序接口,您可以使用WebFilter
应用 interception 样式
过滤器其余处理链和目标之前的和之后的逻辑WebHandler
.使用 WebFlux Config 时,注册一个WebFilter
就是这么简单
将其声明为 Spring Bean 并(可选地)通过使用@Order
上
bean 声明或通过实现Ordered
.
CORS
Spring WebFlux 通过对 CORS 配置的注释提供细粒度的支持
控制器。但是,当您将其与 Spring Security 一起使用时,我们建议依赖内置的CorsFilter
,它必须在 Spring Security 的过滤器链之前订购。
请参阅 CORS 部分和CORSWebFilter
了解更多详情。
URL 处理程序
您可能希望控制器终端节点匹配 URL 路径中带或不带尾部斜杠的路由。
例如,“GET /home” 和 “GET /home/” 都应该由带有@GetMapping("/home")
.
向所有 Map 声明添加尾部斜杠变体并不是处理此用例的最佳方法。
这UrlHandlerFilter
Web 过滤器就是为此目的而设计的。它可以配置为:
-
在接收带有尾部斜杠的 URL 时,以 HTTP 重定向状态响应,将浏览器发送到非尾部斜杠 URL 变体。
-
更改请求,使其就像发送请求时没有尾部斜杠一样,并继续处理请求。
下面介绍如何实例化和配置UrlHandlerFilter
对于博客应用程序:
-
Java
-
Kotlin
UrlHandlerFilter urlHandlerFilter = UrlHandlerFilter
// will HTTP 308 redirect "/blog/my-blog-post/" -> "/blog/my-blog-post"
.trailingSlashHandler("/blog/**").redirect(HttpStatus.PERMANENT_REDIRECT)
// will mutate the request to "/admin/user/account/" and make it as "/admin/user/account"
.trailingSlashHandler("/admin/**").mutateRequest()
.build();
val urlHandlerFilter = UrlHandlerFilter
// will HTTP 308 redirect "/blog/my-blog-post/" -> "/blog/my-blog-post"
.trailingSlashHandler("/blog/**").redirect(HttpStatus.PERMANENT_REDIRECT)
// will mutate the request to "/admin/user/account/" and make it as "/admin/user/account"
.trailingSlashHandler("/admin/**").mutateRequest()
.build()
异常
在WebHandler
应用程序接口,您可以使用WebExceptionHandler
处理
异常WebFilter
instances 和目标WebHandler
.使用 WebFlux Config 时,注册一个WebExceptionHandler
就像将其声明为
Spring Bean 和(可选)通过使用@Order
在 Bean 声明上或
通过实施Ordered
.
下表描述了可用的WebExceptionHandler
实现:
异常处理程序 | 描述 |
---|---|
|
提供对 |
|
扩展 此处理程序在 WebFlux Config 中声明。 |
Codec
这spring-web
和spring-core
modules 支持序列化和
通过非阻塞 I/O 将字节内容反序列化到更高级别的对象或从更高级别的对象反序列化
反应流背压。下面介绍了此支持:
-
HttpMessageReader
和HttpMessageWriter
是合同 对 HTTP 消息内容进行编码和解码。 -
一
Encoder
可以用EncoderHttpMessageWriter
使其适应 Web 中的 application,而Decoder
可以用DecoderHttpMessageReader
. -
DataBuffer
abstracts 不同 字节缓冲区表示形式(例如,NettyByteBuf
,java.nio.ByteBuffer
等)和 is 所有编解码器都适用于什么。请参阅 Data Buffers and Codecs 中的 “Spring Core” 部分,了解有关此主题的更多信息。
这spring-core
module 提供byte[]
,ByteBuffer
,DataBuffer
,Resource
和String
编码器和解码器实现。这spring-web
模块提供 Jackson
JSON、Jackson Smile、JAXB2、Protocol Buffers 和其他编码器和解码器以及
用于表单数据、多部分内容、
server-sent 事件等。
ClientCodecConfigurer
和ServerCodecConfigurer
通常用于配置和
自定义要在应用程序中使用的编解码器。请参阅 配置 HTTP 消息编解码器 部分。
Jackson JSON
JSON 和二进制 JSON (Smile) 是 当存在 Jackson 库时,两者都受支持。
这Jackson2Decoder
工作原理如下:
-
Jackson 的异步、非阻塞解析器用于聚合字节块流 到
TokenBuffer
的 JSON 对象。 -
每
TokenBuffer
传递给 Jackson'sObjectMapper
以创建更高级别的对象。 -
解码为单值发布者(例如
Mono
),则有一个TokenBuffer
. -
解码为多值发布者(例如
Flux
)、每个TokenBuffer
传递给 这ObjectMapper
一旦收到足够多的字节用于完全形成的对象。这 input 内容可以是 JSON 数组,也可以是任何以行分隔的 JSON 格式,例如 NDJSON, JSON 行或 JSON 文本序列。
这Jackson2Encoder
工作原理如下:
-
对于单值发布者(例如
Mono
),只需通过ObjectMapper
. -
对于具有
application/json
,默认情况下,使用Flux#collectToList()
然后序列化生成的集合。 -
对于具有流媒体类型(如
application/x-ndjson
或application/stream+x-jackson-smile
、encode、write 和 使用以行分隔的 JSON 格式单独刷新每个值。其他 流媒体类型可以注册到编码器。 -
对于 SSE ,
Jackson2Encoder
按事件调用,并刷新输出以确保 毫不拖延地交货。
默认情况下,两者都 |
表单数据
FormHttpMessageReader
和FormHttpMessageWriter
支持解码和编码application/x-www-form-urlencoded
内容。
在经常需要从多个位置访问表单内容的服务器端,ServerWebExchange
提供专用的getFormData()
解析内容的方法
通过FormHttpMessageReader
然后缓存结果以供重复访问。
请参阅 表单数据 中的WebHandler
应用程序接口部分。
一次getFormData()
时,无法再从
请求正文。因此,应用程序应通过ServerWebExchange
始终如一地访问缓存的表单数据,而不是从原始请求正文中读取。
多部分
MultipartHttpMessageReader
和MultipartHttpMessageWriter
支持解码和
对“multipart/form-data”、“multipart/mixed”和“multipart/related”内容进行编码。
挨次MultipartHttpMessageReader
委托人到另一个HttpMessageReader
对于实际解析为Flux<Part>
然后简单地将部分收集到MultiValueMap
.
默认情况下,DefaultPartHttpMessageReader
,但这可以通过ServerCodecConfigurer
.
有关DefaultPartHttpMessageReader
,请参阅javadoc 的DefaultPartHttpMessageReader
.
在服务器端,可能需要从多个访问多部分表单内容
地方ServerWebExchange
提供专用的getMultipartData()
解析
内容通过MultipartHttpMessageReader
然后缓存结果以供重复访问。
请参阅 Multipart Data 中的WebHandler
应用程序接口部分。
一次getMultipartData()
时,无法再从
请求正文。因此,应用程序必须始终如一地使用getMultipartData()
对部分进行重复的、类似 Map 的访问,或者依赖SynchronossPartHttpMessageReader
一次性访问Flux<Part>
.
协议缓冲区
ProtobufEncoder
和ProtobufDecoder
支持解码编码 “application/x-protobuf”、“application/octet-stream”
以及 “application/vnd.google.protobuf” 内容com.google.protobuf.Message
类型。它们还支持值流
如果内容是使用内容类型(如 “application/x-protobuf;delimited=true”)。
这需要 3.29 及更高版本的“com.google.protobuf:protobuf-java”库。
这ProtobufJsonDecoder
和ProtobufJsonEncoder
变体支持在 Protobuf 消息中读取和写入 JSON 文档。
它们需要 “com.google.protobuf:protobuf-java-util” 依赖项。请注意,JSON 变体不支持读取消息流,
请参阅javadoc 的ProtobufJsonDecoder
了解更多详情。
限制
Decoder
和HttpMessageReader
缓冲部分或全部 input 的实现
stream 可以配置对内存中缓冲的最大字节数的限制。
在某些情况下,之所以发生缓冲,是因为输入被聚合并表示为单个
object — 例如,具有@RequestBody byte[]
,x-www-form-urlencoded
data 等。流式处理也可能发生缓冲,当
拆分输入流 — 例如,分隔文本、JSON 对象流,以及
等等。对于这些流式处理情况,限制适用于关联的字节数
在流中有一个对象。
要配置缓冲区大小,您可以检查给定的Decoder
或HttpMessageReader
暴露一个maxInMemorySize
property 属性,如果是这样,Javadoc 将包含有关 default 的详细信息
值。在服务器端,ServerCodecConfigurer
提供从
设置所有编解码器,请参阅 HTTP 消息编解码器。在客户端,对
所有编解码器都可以在 WebClient.Builder 中更改。
对于 Multipart 解析,maxInMemorySize
属性限制
非文件部分的大小。对于文件部分,它确定部分达到的阈值
已写入磁盘。对于写入磁盘的文件部分,还有一个额外的maxDiskUsagePerPart
属性来限制每个部分的磁盘空间量。还有
一个maxParts
属性来限制 Multipart 请求中的段总数。
要在 WebFlux 中配置所有这三个实例,您需要提供一个预配置的MultipartHttpMessageReader
自ServerCodecConfigurer
.
流
当流式传输到 HTTP 响应(例如text/event-stream
,application/x-ndjson
),请务必定期发送数据,以便
尽早可靠地检测断开连接的客户端。这样的发送可以是
仅注释、空 SSE 事件或任何其他“无作”数据,这些数据实际上可以用作
心跳。
DataBuffer
DataBuffer
是 WebFlux 中字节缓冲区的表示形式。Spring Core 的
此参考在 Data Buffers and Codecs 一节中提供了更多相关信息。要理解的关键点是,在某些
像 Netty 这样的服务器,字节缓冲区是池化的和引用计数的,并且必须释放
当使用以避免内存泄漏时。
WebFlux 应用程序通常不需要关心此类问题,除非它们 直接使用或生成数据缓冲区,而不是依赖编解码器转换为 以及更高级别的对象,或者除非他们选择创建自定义编解码器。对于这样的 情况请查看 Data Buffers 和 Codecs、 尤其是 使用 DataBuffer 的部分。
Logging
DEBUG
Spring WebFlux 中的级别日志记录被设计为紧凑、最小和
人性化。它侧重于有用的高价值信息
over again 与仅在调试特定问题时有用的其他 cookie 进行比较。
TRACE
级别日志记录通常遵循与DEBUG
(例如,还有
不应是 Firehose),但可用于调试任何问题。此外,一些日志
消息可能会在TRACE
与DEBUG
.
良好的日志记录来自使用日志的经验。如果您发现任何 未达到既定目标,请告诉我们。
日志 ID
在 WebFlux 中,单个请求可以在多个线程上运行,线程 ID 对于关联属于特定请求的日志消息没有用。这就是为什么 默认情况下,WebFlux 日志消息以特定于请求的 ID 为前缀。
在服务器端,日志 ID 存储在ServerWebExchange
属性
(LOG_ID_ATTRIBUTE
),
而基于该 ID 的完全格式化前缀可从ServerWebExchange#getLogPrefix()
.在WebClient
端,日志 ID 存储在ClientRequest
属性
(LOG_ID_ATTRIBUTE
)
,而完全格式化的前缀可从ClientRequest#logPrefix()
.
敏感数据
DEBUG
和TRACE
日志记录可以记录敏感信息。这就是为什么表单参数和
默认情况下,标头是屏蔽的,您必须显式启用其日志记录。
以下示例显示了如何对服务器端请求执行此作:
-
Java
-
Kotlin
@Configuration
@EnableWebFlux
class MyConfig implements WebFluxConfigurer {
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.defaultCodecs().enableLoggingRequestDetails(true);
}
}
@Configuration
@EnableWebFlux
class MyConfig : WebFluxConfigurer {
override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
configurer.defaultCodecs().enableLoggingRequestDetails(true)
}
}
以下示例说明如何对客户端请求执行此作:
-
Java
-
Kotlin
Consumer<ClientCodecConfigurer> consumer = configurer ->
configurer.defaultCodecs().enableLoggingRequestDetails(true);
WebClient webClient = WebClient.builder()
.exchangeStrategies(strategies -> strategies.codecs(consumer))
.build();
val consumer: (ClientCodecConfigurer) -> Unit = { configurer -> configurer.defaultCodecs().enableLoggingRequestDetails(true) }
val webClient = WebClient.builder()
.exchangeStrategies({ strategies -> strategies.codecs(consumer) })
.build()
附加程序
日志记录库(如 SLF4J 和 Log4J 2)提供了异步记录器,这些记录器可避免 阻塞。虽然这些有其自身的缺点,例如可能会丢弃消息 无法排队进行日志记录,它们是目前最好的可用选项 用于反应式、非阻塞应用程序。
自定义编解码器
应用程序可以注册自定义编解码器以支持其他媒体类型、 或默认编解码器不支持的特定行为。
以下示例说明如何对客户端请求执行此作:
-
Java
-
Kotlin
WebClient webClient = WebClient.builder()
.codecs(configurer -> {
CustomDecoder decoder = new CustomDecoder();
configurer.customCodecs().registerWithDefaultConfig(decoder);
})
.build();
val webClient = WebClient.builder()
.codecs({ configurer ->
val decoder = CustomDecoder()
configurer.customCodecs().registerWithDefaultConfig(decoder)
})
.build()