此版本仍在开发中,尚未稳定。如需最新的稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

Reactive Core

The spring-web 模块包含以下用于响应式 Web 应用程序的基础支持:spring-doc.cadn.net.cn

HttpHandler

HttpHandler 是一个简单的契约,具有一个用于处理请求和响应的方法。它是故意最小化的,其主要和唯一目的是作为不同HTTP服务器API的最小抽象。spring-doc.cadn.net.cn

下表描述了支持的服务器API:spring-doc.cadn.net.cn

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

Netty 翻译为:内特spring-doc.cadn.net.cn

Netty APIspring-doc.cadn.net.cn

Reactor Nettyspring-doc.cadn.net.cn

Undertowspring-doc.cadn.net.cn

Undertow APIspring-doc.cadn.net.cn

spring-web:Undertow 到 Reactive Streams 的桥接spring-doc.cadn.net.cn

Tomcatspring-doc.cadn.net.cn

Servlet非阻塞I/O;Tomcat API支持读写ByteBuffers与byte[]的对比spring-doc.cadn.net.cn

spring-web:Servlet非阻塞I/O到Reactive Streams的桥接spring-doc.cadn.net.cn

Jettyspring-doc.cadn.net.cn

Servlet 非阻塞 I/O;Jetty API 支持直接写入 ByteBuffers 而非 byte[] 数组。spring-doc.cadn.net.cn

spring-web:Servlet非阻塞I/O到Reactive Streams的桥接spring-doc.cadn.net.cn

Servlet容器spring-doc.cadn.net.cn

Servlet非阻塞I/Ospring-doc.cadn.net.cn

spring-web:Servlet非阻塞I/O到Reactive Streams的桥接spring-doc.cadn.net.cn

以下表格描述了服务器依赖项(另见 支持的版本):spring-doc.cadn.net.cn

服务器名称 组 ID 构件名称

Reactor Nettyspring-doc.cadn.net.cn

io.projectreactor.nettyspring-doc.cadn.net.cn

reactor-nettyspring-doc.cadn.net.cn

Undertowspring-doc.cadn.net.cn

io.undertowspring-doc.cadn.net.cn

undertow-corespring-doc.cadn.net.cn

Tomcatspring-doc.cadn.net.cn

org.apache.tomcat.embedspring-doc.cadn.net.cn

tomcat-embed-corespring-doc.cadn.net.cn

Jettyspring-doc.cadn.net.cn

org.eclipse.jettyspring-doc.cadn.net.cn

jetty-server,jetty-servletspring-doc.cadn.net.cn

以下代码片段展示了使用HttpHandler适配器与每个服务器API的结合:spring-doc.cadn.net.cn

Reactor Nettyspring-doc.cadn.net.cn

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()
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()
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()
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容器spring-doc.cadn.net.cn

要作为WAR文件部署到任何Servlet容器中,您可以扩展并包含 AbstractReactiveWebInitializer 在WAR中。该类使用ServletHttpHandlerAdapter封装HttpHandler,并将其注册为 Servletspring-doc.cadn.net.cn

WebHandler 应用程序接口

The org.springframework.web.server 包基于 HttpHandler 合同 提供一个通用的Web API,用于通过多个 WebExceptionHandler、多个 WebFilter 和一个单一的 WebHandler 组件处理请求。该链可以通过 WebHttpHandlerBuilder 简单地指向一个 Spring ApplicationContext 来组合,在那里组件是 自动检测 的,并/或通过向构建器注册组件。spring-doc.cadn.net.cn

While HttpHandler 有一个简单的目标,即抽象不同 HTTP 服务器的使用,WebHandler API 旨在提供一组在 Web 应用程序中常用的功能,例如:spring-doc.cadn.net.cn

特殊bean类型

以下表格列出了WebHttpHandlerBuilder可以在Spring ApplicationContext中自动检测的组件,或者可以直接注册到其中的组件:spring-doc.cadn.net.cn

豆名称 Beans类型 计数 描述

任何spring-doc.cadn.net.cn

WebExceptionHandlerspring-doc.cadn.net.cn

0..Nspring-doc.cadn.net.cn

为来自WebFilter实例链和目标WebHandler的异常提供处理。有关更多详细信息,请参见异常spring-doc.cadn.net.cn

任何spring-doc.cadn.net.cn

WebFilterspring-doc.cadn.net.cn

0..Nspring-doc.cadn.net.cn

将拦截样式逻辑应用于过滤器链的其余部分和目标WebHandler之前和之后。有关详细信息,请参阅过滤器spring-doc.cadn.net.cn

webHandlerspring-doc.cadn.net.cn

WebHandlerspring-doc.cadn.net.cn

1spring-doc.cadn.net.cn

处理请求的处理器。spring-doc.cadn.net.cn

webSessionManagerspring-doc.cadn.net.cn

WebSessionManagerspring-doc.cadn.net.cn

0..1spring-doc.cadn.net.cn

管理器通过 ServerWebExchange 上的方法暴露了 WebSession 个实例。 默认为 DefaultWebSessionManagerspring-doc.cadn.net.cn

serverCodecConfigurerspring-doc.cadn.net.cn

ServerCodecConfigurerspring-doc.cadn.net.cn

0..1spring-doc.cadn.net.cn

对于访问 HttpMessageReader 实例以解析表单数据和多部分数据,然后通过 ServerWebExchange 上的方法进行暴露。默认情况下为 ServerCodecConfigurer.create()spring-doc.cadn.net.cn

localeContextResolverspring-doc.cadn.net.cn

LocaleContextResolverspring-doc.cadn.net.cn

0..1spring-doc.cadn.net.cn

The resolver for LocaleContext exposed through a method on ServerWebExchange. AcceptHeaderLocaleContextResolver by default.spring-doc.cadn.net.cn

forwardedHeaderTransformerspring-doc.cadn.net.cn

ForwardedHeaderTransformerspring-doc.cadn.net.cn

0..1spring-doc.cadn.net.cn

对于转发类型标头的处理,可以通过提取并删除它们或仅删除它们来实现。默认情况下不使用。spring-doc.cadn.net.cn

表单数据

ServerWebExchange 提供了以下方法来访问表单数据:spring-doc.cadn.net.cn

Mono<MultiValueMap<String, String>> getFormData();
suspend fun getFormData(): MultiValueMap<String, String>

The DefaultServerWebExchange 使用配置的 HttpMessageReader 来解析表单数据 (application/x-www-form-urlencoded) 成为一个 MultiValueMap. 默认情况下, FormHttpMessageReader 被配置用于 ServerCodecConfigurer bean (参见 Web Handler API)。spring-doc.cadn.net.cn

多部分数据

ServerWebExchange 提供了以下方法来访问多部分数据:spring-doc.cadn.net.cn

Mono<MultiValueMap<String, Part>> getMultipartData();
suspend fun getMultipartData(): MultiValueMap<String, Part>

DefaultServerWebExchange使用配置的 HttpMessageReader<MultiValueMap<String, Part>>multipart/form-datamultipart/mixedmultipart/related内容解析为MultiValueMap。 默认情况下,这是DefaultPartHttpMessageReader,它没有任何第三方依赖。 或者,可以使用SynchronossPartHttpMessageReader,它基于 Synchronoss NIO Multipart库。 两者都通过ServerCodecConfigurer bean进行配置 (请参阅Web处理程序API)。spring-doc.cadn.net.cn

要以流式处理方式解析多部分数据,您可以使用Flux<PartEvent>(从PartEventHttpMessageReader返回)而非@RequestPart, 因为后者意味着需要像Map那样按名称访问各个部分, 从而要求完整解析多部分数据。相比之下,使用@RequestBody可将内容解码为Flux<PartEvent>, 而无需收集到MultiValueMap中。spring-doc.cadn.net.cn

请求头转发

当请求经过负载均衡器等代理时,主机、端口和方案可能会发生变化,这使得从客户端角度创建指向正确主机、端口和方案的链接变得具有挑战性。spring-doc.cadn.net.cn

RFC 7239 定义了 Forwarded HTTP 头, 代理可以使用该头来提供有关原始请求的信息。spring-doc.cadn.net.cn

非标准头部

还有一些非标准的头信息,包括X-Forwarded-HostX-Forwarded-PortX-Forwarded-ProtoX-Forwarded-SslX-Forwarded-Prefixspring-doc.cadn.net.cn

X-Forwarded-Host

尽管不是标准,X-Forwarded-Host: <host> 实际上已成为一种标准头部,用于向下游服务器传递原始主机信息。例如,如果向代理发送了对 example.com/resource 的请求,而该代理将请求转发到 localhost:8080/resource,则可以发送一个 X-Forwarded-Host: example.com 的头部,以告知服务器原始主机是 example.comspring-doc.cadn.net.cn

X-Forwarded-端口

尽管不是标准,X-Forwarded-Port: <port> 是一个事实上的标准头部,用于向下游服务器传递原始端口信息。例如,如果一个指向 example.com/resource 的请求被发送到代理服务器,该代理将请求转发至 localhost:8080/resource,那么可以发送一个值为 X-Forwarded-Port: 443 的头部,以告知服务器原始端口是 443spring-doc.cadn.net.cn

X-Forwarded-Proto

虽然并非标准,但X-Forwarded-Proto: (https|http)作为一个事实上的标准头部,用于向下游服务器传达原始协议(例如,https / http)。 例如,如果将请求example.com/resource发送到代理,该代理将请求转发到localhost:8080/resource,则可以发送头部X-Forwarded-Proto: https来告知服务器原始协议是httpsspring-doc.cadn.net.cn

X-Forwarded-Ssl

虽然并非标准,但X-Forwarded-Ssl: (on|off)是一个实际中广泛使用的头部,用于向下游服务器传达原始协议(例如,https / http)。例如,如果将example.com/resource的请求发送到代理,该代理将请求转发到localhost:8080/resource,则使用X-Forwarded-Ssl: on头部来告知服务器原始协议是httpsspring-doc.cadn.net.cn

X-Forwarded-Prefix

尽管不是标准,X-Forwarded-Prefix: <prefix> 实际上已成为一种标准头部,用于向下游服务器传递原始URL路径前缀。spring-doc.cadn.net.cn

使用X-Forwarded-Prefix可能因部署场景而异,需要保持灵活性以允许替换、移除或在目标服务器的路径前缀前追加。spring-doc.cadn.net.cn

场景1:覆盖路径前缀spring-doc.cadn.net.cn

https://example.com/api/{path} -> http://localhost:8080/app1/{path}

前缀是捕获组 {path} 之前路径的开头部分。对于代理,前缀是 /api,而对于服务器,前缀是 /app1。在这种情况下,代理可以发送 X-Forwarded-Prefix: /api,以使原始前缀 /api 覆盖服务器前缀 /app1spring-doc.cadn.net.cn

场景2:移除路径前缀spring-doc.cadn.net.cn

有时,应用程序可能希望移除该前缀。例如,考虑以下代理到服务器的映射:spring-doc.cadn.net.cn

https://app1.example.com/{path} -> http://localhost:8080/app1/{path}
https://app2.example.com/{path} -> http://localhost:8080/app2/{path}

代理没有前缀,而应用程序 app1app2 分别具有路径前缀 /app1/app2。代理可以发送 X-Forwarded-Prefix: 以使空前缀覆盖服务器前缀 /app1/app2spring-doc.cadn.net.cn

此部署场景的一个常见情况是许可证按生产应用服务器计费,因此更倾向于在每台服务器上部署多个应用程序以降低费用。另一个原因是在同一台服务器上运行更多应用程序,以便共享服务器运行所需资源。spring-doc.cadn.net.cn

在这些场景中,应用程序需要一个非空的上下文根路径,因为同一台服务器上存在多个应用程序。然而,在公共 API 的 URL 路径中不应暴露此上下文路径,应用程序可以使用不同的子域名,从而带来如下好处:spring-doc.cadn.net.cn

场景3:插入路径前缀spring-doc.cadn.net.cn

在其他情况下,可能需要添加一个前缀。例如,考虑以下代理到服务器的映射:spring-doc.cadn.net.cn

https://example.com/api/app1/{path} -> http://localhost:8080/app1/{path}

在这种情况下,代理的前缀为 /api/app1,服务器的前缀为 /app1。代理可以发送 X-Forwarded-Prefix: /api/app1,使原始前缀 /api/app1 覆盖服务器前缀 /app1spring-doc.cadn.net.cn

转发头转换器

ForwardedHeaderTransformer 是一个组件,它根据请求头转发修改请求的主机、端口和协议,然后删除这些标头。如果你将其声明为名称为 forwardedHeaderTransformer 的 bean,它将被 检测到 并使用。spring-doc.cadn.net.cn

在5.1中,ForwardedHeaderFilter已被弃用并由ForwardedHeaderTransformer取代,因此可以在创建交换之前更早地处理转发的头信息。如果已配置了该过滤器,它将从过滤器列表中移除,并改用ForwardedHeaderTransformer

安全注意事项

转发头信息存在安全问题,因为应用程序无法确定这些头信息是由代理按预期添加的,还是由恶意客户端添加的。这就是为什么在信任边界的代理应配置为删除来自外部的不可信转发流量。您还可以将 ForwardedHeaderTransformer 配置为 removeOnly=true,在这种情况下,会删除但不使用这些头信息。spring-doc.cadn.net.cn

过滤器

WebHandler API 中,你可以使用 WebFilter 来应用拦截风格的逻辑,在过滤器链和目标 WebHandler 的其余处理链之前和之后。当使用 WebFlux 配置 时,注册一个 WebFilter 只需将其声明为 Spring bean,并(可选地)通过在 bean 声明上使用 @Order 或实现 Ordered 来表达优先级。spring-doc.cadn.net.cn

CORS

Spring WebFlux 通过注解在控制器上提供了对 CORS 配置的细粒度支持。但是,当你将其与 Spring Security 一起使用时,我们建议依赖内置的 CorsFilter,它必须在 Spring Security 的过滤器链之前进行排序。spring-doc.cadn.net.cn

查看关于 CORS 的章节以及 CORS WebFilter 的更多信息。spring-doc.cadn.net.cn

URL 处理器

您可能希望控制器端点匹配URL路径中带或不带尾随斜杠的路由。 例如,“GET /home”和“GET /home/”都应由带有@RequestMapping注解的控制器方法处理。spring-doc.cadn.net.cn

Spring 提供了 UrlHandlerFilter,该功能从URL路径中移除尾随斜杠,确保带或不带尾随斜杠的路径视图一致。 这是为了避免基于URL的授权决策与Web框架请求映射之间的不匹配问题非常重要。 过滤器可以通过以下几种方式之一移除尾随斜杠:spring-doc.cadn.net.cn

以下是为博客应用程序实例化和配置UrlHandlerFilter的方法:spring-doc.cadn.net.cn

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

请谨记以下事项:spring-doc.cadn.net.cn

  • 根路径 "/" 不受尾部斜杠处理的影响。spring-doc.cadn.net.cn

  • @RequestMapping("/") 为类型级别的映射添加尾随斜杠,因此在处理尾随斜杠时不会映射;请改用 @RequestMapping(无路径属性)。spring-doc.cadn.net.cn

异常

WebHandler API 中,你可以使用一个 WebExceptionHandler 来处理来自一系列 WebFilter 实例和目标 WebHandler 的异常。当使用 WebFlux 配置 时,注册一个 WebExceptionHandler 只需将其声明为 Spring bean,并(可选地)通过在 bean 声明上使用 @Order 或实现 Ordered 来表达优先级。spring-doc.cadn.net.cn

以下表格描述了可用的WebExceptionHandler实现:spring-doc.cadn.net.cn

异常处理 描述

ResponseStatusExceptionHandlerspring-doc.cadn.net.cn

为类型为 ResponseStatusException 的异常提供处理,通过将响应设置为异常的HTTP状态代码。spring-doc.cadn.net.cn

WebFluxResponseStatusExceptionHandlerspring-doc.cadn.net.cn

Extension of ResponseStatusExceptionHandler that can also determine the HTTP status code of a @ResponseStatus annotation on any exception.spring-doc.cadn.net.cn

此处理器在WebFlux Config中声明。spring-doc.cadn.net.cn

编解码器

The spring-webspring-core 模块提供了通过非阻塞 I/O 与 Reactive Streams 背压进行序列化和反序列化字节内容到高级对象的支持。以下描述了这种支持:spring-doc.cadn.net.cn

The spring-core 模块提供了 byte[]ByteBufferDataBufferResource、和 String 编码器和解码器实现。The spring-web 模块提供了 Jackson JSON、Jackson Smile、JAXB2、Protocol Buffers 以及其他编码器和解码器,同时还提供了 仅限 Web 的 HTTP 消息读取器和写入器实现,用于表单数据、多部分内容、服务器发送的事件等。spring-doc.cadn.net.cn

ClientCodecConfigurerServerCodecConfigurer 通常用于配置和 自定义要在应用程序中使用的编解码器。请参见有关配置 HTTP 消息编解码器的部分。spring-doc.cadn.net.cn

Jackson JSON

JSON 和二进制 JSON (Smile) 都在 Jackson 库存在时得到支持。spring-doc.cadn.net.cn

The Jackson2Decoder works as follows:spring-doc.cadn.net.cn

  • Jackson的异步、非阻塞解析器用于聚合字节块流 成TokenBuffer,每个代表一个JSON对象。spring-doc.cadn.net.cn

  • 每个 TokenBuffer 都被传递给 Jackson 的 ObjectMapper 以创建一个更高级的对象。spring-doc.cadn.net.cn

  • 在解码为单值发布者(例如,Mono)时,存在一个TokenBufferspring-doc.cadn.net.cn

  • 在解码为多值发布者(例如,Flux)时,一旦收到足够的字节构成一个完整对象,每个TokenBuffer将立即传递给ObjectMapper。输入内容可以是JSON数组,或任何 行分隔的JSON格式,如NDJSON、JSON Lines或JSON文本序列。spring-doc.cadn.net.cn

The Jackson2Encoder works as follows:spring-doc.cadn.net.cn

  • 对于单值发布者(例如,Mono),只需通过 ObjectMapper 对其进行序列化即可。spring-doc.cadn.net.cn

  • 对于具有application/json的多值发布者,默认情况下收集具有Flux#collectToList()的值,然后序列化生成的集合。spring-doc.cadn.net.cn

  • 对于具有流媒体类型(如 application/x-ndjsonapplication/stream+x-jackson-smile)的多值发布者,使用 换行分隔的JSON 格式分别对每个值进行编码、写入和刷新。其他流媒体类型可以注册到编码器中。spring-doc.cadn.net.cn

  • 对于SSE,Jackson2Encoder会在每个事件中被调用,并且输出会被立即刷新以确保无延迟交付。spring-doc.cadn.net.cn

默认情况下,Jackson2EncoderJackson2Decoder 都不支持类型为 String 的元素。相反,默认假设是一个字符串或字符串序列表示序列化的 JSON 内容,将由 CharSequenceEncoder 渲染。如果你需要从 Flux<String> 渲染 JSON 数组,请使用 Flux#collectToList() 并编码一个 Mono<List<String>>spring-doc.cadn.net.cn

表单数据

FormHttpMessageReaderFormHttpMessageWriter 支持解码和编码 application/x-www-form-urlencoded 内容。spring-doc.cadn.net.cn

在服务器端,表单内容经常需要从多个地方访问, ServerWebExchange 提供了一个专门的 getFormData() 方法,该方法通过 FormHttpMessageReader 解析内容 然后缓存结果以供重复访问。 请参见 表单数据WebHandler API 部分。spring-doc.cadn.net.cn

一旦使用了getFormData(),就无法再从请求体中读取原始的原始内容。出于这个原因,应用程序应该始终通过ServerWebExchange来访问缓存的表单数据,而不是从原始请求体中读取。spring-doc.cadn.net.cn

多部分

MultipartHttpMessageReaderMultipartHttpMessageWriter支持对“multipart/form-data”、“multipart/mixed”和“multipart/related”内容进行解码和编码。 接着MultipartHttpMessageReader委托另一个HttpMessageReader将内容实际解析为Flux<Part>,然后简单地将各个部分收集到MultiValueMap中。 默认情况下,使用DefaultPartHttpMessageReader,但可以通过ServerCodecConfigurer更改。 有关DefaultPartHttpMessageReader的更多信息,请参阅DefaultPartHttpMessageReader的javadocspring-doc.cadn.net.cn

在服务器端,当可能需要从多个地方访问multipart表单内容时,ServerWebExchange 提供了一个专门的 getMultipartData() 方法,该方法通过 MultipartHttpMessageReader 解析内容,然后缓存结果以供重复访问。请参见 Multipart DataWebHandler API 部分。spring-doc.cadn.net.cn

一旦使用了getMultipartData(),就无法再从请求体中读取原始的原始内容。因此,应用程序必须始终使用getMultipartData()来重复、类似映射地访问部分,或者依赖于SynchronossPartHttpMessageReader进行一次性访问Flux<Part>spring-doc.cadn.net.cn

协议缓冲区

ProtobufEncoderProtobufDecoder 支持为 com.google.protobuf.Message 类型解码和编码 "application/x-protobuf"、"application/octet-stream" 以及 "application/vnd.google.protobuf" 格式的内容。如果内容随内容类型一起接收/发送时带有 "delimited" 参数(如 "application/x-protobuf;delimited=true"),它们还支持值的流式传输。 这需要 "com.google.protobuf:protobuf-java" 库,版本 3.29 及以上。spring-doc.cadn.net.cn

ProtobufJsonDecoderProtobufJsonEncoder 变体支持将JSON文档读取和写入到Protobuf消息中。它们需要"com.google.protobuf:protobuf-java-util"依赖。请注意,JSON变体不支持读取消息流,更多详细信息请参见ProtobufJsonDecoder的javadocspring-doc.cadn.net.cn

限制

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

要配置缓冲区大小,您可以检查给定的DecoderHttpMessageReader是否暴露了maxInMemorySize属性,如果暴露了,Javadoc将提供有关默认值的详细信息。在服务器端,ServerCodecConfigurer提供了一个设置所有编解码器的单一位置,详情请参见HTTP消息编解码器。在客户端,可以在WebClient.Builder中更改所有编解码器的限制。spring-doc.cadn.net.cn

对于 多部分解析maxInMemorySize 属性限制了非文件部分的大小。对于文件部分,它确定了将部分写入磁盘的阈值。对于写入磁盘的文件部分,还有一个 maxDiskUsagePerPart 属性来限制每个部分的磁盘空间量。还有一个 maxParts 属性来限制multipart请求中的部分总数。要在WebFlux中配置这三个属性,您需要向 ServerCodecConfigurer 提供一个预配置的 MultipartHttpMessageReader 实例。spring-doc.cadn.net.cn

流式传输

当向HTTP响应流式传输数据(例如,text/event-stream, application/x-ndjson)时,定期发送数据非常重要,以便能够尽早可靠地检测到已断开连接的客户端。这种发送可以是一个仅包含注释的空SSE事件或任何其他“无操作”数据,这些数据实际上可以作为心跳。spring-doc.cadn.net.cn

DataBuffer

DataBuffer 是 WebFlux 中字节缓冲区的表示。Spring Core 部分的参考文档在 数据缓冲区和编解码器一节中有更多相关内容。关键点是理解在某些服务器(如 Netty)上,字节缓冲区是被池化和引用计数的,并且在使用后必须释放以避免内存泄漏。spring-doc.cadn.net.cn

WebFlux应用程序通常不需要关心此类问题,除非它们直接消费或生成数据缓冲区,而不是依赖编解码器将数据转换为更高层次的对象,或者除非它们选择创建自定义编解码器。对于此类情况,请参阅数据缓冲区和编解码器中的信息,特别是使用DataBuffer部分。spring-doc.cadn.net.cn

日志

DEBUG 级别日志记录在 Spring WebFlux 中设计为紧凑、简约且人性化。它专注于那些反复有用的高度有价值的信息,而不是仅在调试特定问题时有用的信息。spring-doc.cadn.net.cn

TRACE 级别日志通常遵循与 DEBUG(例如也不应该是数据流)相同的原则,但可以用于调试任何问题。此外,某些日志消息在 TRACEDEBUG 级别之间可能会显示不同的详细程度。spring-doc.cadn.net.cn

良好的日志记录来自于使用日志的经验。如果您发现任何不符合既定目标的情况,请告知我们。spring-doc.cadn.net.cn

日志ID

在WebFlux中,一个请求可以在多个线程上运行,并且线程ID对于关联属于特定请求的日志消息没有用处。这就是为什么WebFlux日志消息默认会以请求特定的ID作为前缀。spring-doc.cadn.net.cn

在服务器端,日志ID存储在ServerWebExchange属性中 (LOG_ID_ATTRIBUTE), 而基于该ID的完全格式化的前缀可以从ServerWebExchange#getLogPrefix()获取。在WebClient端,日志ID存储在ClientRequest属性中 (LOG_ID_ATTRIBUTE) ,而一个完全格式化的前缀可以从ClientRequest#logPrefix()获取。spring-doc.cadn.net.cn

敏感数据

DEBUGTRACE 级别的日志可能会记录敏感信息。这就是为什么默认情况下会屏蔽表单参数和标头,您必须显式启用它们的完整日志记录。spring-doc.cadn.net.cn

以下示例展示了如何对服务器端请求进行操作:spring-doc.cadn.net.cn

@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)
	}
}

以下示例展示了如何对客户端请求进行操作:spring-doc.cadn.net.cn

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 等日志库可以提供异步日志记录器,以避免阻塞。虽然这些库有自己的缺点,例如可能会丢失无法排队的日志消息,但它们目前是用于响应式、非阻塞应用程序的最佳可用选项。spring-doc.cadn.net.cn

自定义编解码器

应用程序可以注册自定义编解码器,以支持额外的媒体类型,或者默认编解码器不支持的特定行为。spring-doc.cadn.net.cn

一些由开发人员表达的配置选项会在默认编解码器上强制执行。 自定义编解码器可能希望有机会与这些偏好保持一致, 例如强制执行缓冲限制记录敏感数据spring-doc.cadn.net.cn

以下示例展示了如何对客户端请求进行操作:spring-doc.cadn.net.cn

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