反應式核心

spring-web 模組包含以下針對反應式 Web 應用程式的基本支援

  • 對於伺服器請求處理,有兩個層級的支援。

    • HttpHandler:HTTP 請求處理的基本契約,具有非阻塞 I/O 和反應式串流回壓,以及 Reactor Netty、Undertow、Tomcat、Jetty 和任何 Servlet 容器的適配器。

    • WebHandler API:稍微高階、通用的 Web API,用於請求處理,在其之上建構了具體的程式設計模型,例如註解控制器和函數式端點。

  • 對於用戶端,有一個基本的 ClientHttpConnector 契約,用於執行具有非阻塞 I/O 和反應式串流回壓的 HTTP 請求,以及 Reactor Netty、反應式 Jetty HttpClientApache HttpComponents 的適配器。應用程式中使用的更高階 WebClient 建構於此基本契約之上。

  • 對於用戶端和伺服器,用於序列化和反序列化 HTTP 請求和回應內容的 編解碼器

HttpHandler

HttpHandler 是一個簡單的契約,具有單一方法來處理請求和回應。它刻意保持最小化,其主要且唯一目的是作為不同 HTTP 伺服器 API 的最小抽象化。

下表描述了支援的伺服器 API

伺服器名稱 使用的伺服器 API 反應式串流支援

Netty

Netty API

Reactor Netty

Undertow

Undertow API

spring-web:Undertow 到反應式串流橋接器

Tomcat

Servlet 非阻塞 I/O;Tomcat API 用於讀取和寫入 ByteBuffers 而非 byte[]

spring-web:Servlet 非阻塞 I/O 到反應式串流橋接器

Jetty

Servlet 非阻塞 I/O;Jetty API 用於寫入 ByteBuffers 而非 byte[]

spring-web:Servlet 非阻塞 I/O 到反應式串流橋接器

Servlet 容器

Servlet 非阻塞 I/O

spring-web:Servlet 非阻塞 I/O 到反應式串流橋接器

下表描述了伺服器相依性(另請參閱 支援的版本

伺服器名稱 群組 ID Artifact 名稱

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

以下程式碼片段顯示了將 HttpHandler 適配器與每個伺服器 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 在 WAR 中。該類別使用 ServletHttpHandlerAdapter 包裝 HttpHandler,並將其註冊為 Servlet

WebHandler API

org.springframework.web.server 套件建構於 HttpHandler 契約之上,以透過多個 WebExceptionHandler、多個 WebFilter 和單一 WebHandler 組件的鏈結來處理請求,從而提供通用的 Web API。鏈結可以使用 WebHttpHandlerBuilder 組裝在一起,方法是簡單地指向 Spring ApplicationContext,組件在其中 自動偵測,和/或使用建構器註冊組件。

雖然 HttpHandler 的簡單目標是抽象化不同 HTTP 伺服器的使用,但 WebHandler API 旨在提供更廣泛的功能集,這些功能通常在 Web 應用程式中使用,例如

  • 具有屬性的使用者 Session。

  • 請求屬性。

  • 針對請求解析的 LocalePrincipal

  • 存取已剖析和快取的表單資料。

  • Multipart 資料的抽象化。

  • 以及更多..

特殊 Bean 類型

下表列出了 WebHttpHandlerBuilder 可以在 Spring ApplicationContext 中自動偵測或直接向其註冊的組件

Bean 名稱 Bean 類型 計數 描述

<任何>

WebExceptionHandler

0..N

為來自 WebFilter 實例鏈結和目標 WebHandler 的例外狀況提供處理。如需更多詳細資訊,請參閱 例外狀況

<任何>

WebFilter

0..N

將攔截樣式邏輯套用至篩選器鏈結的其餘部分和目標 WebHandler 之前和之後。如需更多詳細資訊,請參閱 篩選器

webHandler

WebHandler

1

請求的處理常式。

webSessionManager

WebSessionManager

0..1

透過 ServerWebExchange 上的方法公開的 WebSession 實例的管理員。預設為 DefaultWebSessionManager

serverCodecConfigurer

ServerCodecConfigurer

0..1

用於存取 HttpMessageReader 實例,以剖析表單資料和 multipart 資料,然後透過 ServerWebExchange 上的方法公開。預設為 ServerCodecConfigurer.create()

localeContextResolver

LocaleContextResolver

0..1

用於解析 LocaleContext 的解析器,透過 ServerWebExchange 上的方法公開。預設為 AcceptHeaderLocaleContextResolver

forwardedHeaderTransformer

ForwardedHeaderTransformer

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 Bean 使用(請參閱 Web Handler API)。

Multipart 資料

ServerWebExchange 公開了以下方法來存取 multipart 資料

  • Java

  • Kotlin

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 Handler API)。

若要以串流方式剖析 multipart 資料,您可以使用從 PartEventHttpMessageReader 傳回的 Flux<PartEvent>,而不是使用 @RequestPart,因為這表示類似 Map 的方式存取依名稱區分的個別部分,因此需要完整剖析 multipart 資料。相反地,您可以使用 @RequestBody 將內容解碼為 Flux<PartEvent>,而無需收集到 MultiValueMap

轉發的標頭

當請求通過負載平衡器等 Proxy 時,主機、連接埠和 Scheme 可能會變更,這使得從用戶端角度建立指向正確主機、連接埠和 Scheme 的連結成為挑戰。

RFC 7239 定義了 Forwarded HTTP 標頭,Proxy 可以使用它來提供有關原始請求的資訊。

非標準標頭

還有其他非標準標頭,包括 X-Forwarded-HostX-Forwarded-PortX-Forwarded-ProtoX-Forwarded-SslX-Forwarded-Prefix

X-Forwarded-Host

雖然不是標準,但 X-Forwarded-Host: <host> 是一個事實上的標準標頭,用於將原始主機傳達給下游伺服器。例如,如果將 example.com/resource 的請求傳送到 Proxy,Proxy 將請求轉發到 localhost:8080/resource,則可以傳送 X-Forwarded-Host: example.com 標頭,以告知伺服器原始主機是 example.com

X-Forwarded-Port

雖然不是標準,但 X-Forwarded-Port: <port> 是一個事實上的標準標頭,用於將原始連接埠傳達給下游伺服器。例如,如果將 example.com/resource 的請求傳送到 Proxy,Proxy 將請求轉發到 localhost:8080/resource,則可以傳送 X-Forwarded-Port: 443 標頭,以告知伺服器原始連接埠是 443

X-Forwarded-Proto

雖然不是標準,但 X-Forwarded-Proto: (https|http) 是一個事實上的標準標頭,用於將原始協定(例如,https/http)傳達給下游伺服器。例如,如果將 example.com/resource 的請求傳送到 Proxy,Proxy 將請求轉發到 localhost:8080/resource,則可以傳送 X-Forwarded-Proto: https 標頭,以告知伺服器原始協定是 https

X-Forwarded-Ssl

雖然不是標準,但 X-Forwarded-Ssl: (on|off) 是一個事實上的標準標頭,用於將原始協定(例如,https/http)傳達給下游伺服器。例如,如果將 example.com/resource 的請求傳送到 Proxy,Proxy 將請求轉發到 localhost:8080/resource,則可以傳送 X-Forwarded-Ssl: on 標頭,以告知伺服器原始協定是 https

X-Forwarded-Prefix

雖然不是標準,但 X-Forwarded-Prefix: <prefix> 是一個事實上的標準標頭,用於將原始 URL 路徑前綴傳達給下游伺服器。

X-Forwarded-Prefix 的使用可能因部署情境而異,並且需要具有彈性,以允許取代、移除或預先附加目標伺服器的路徑前綴。

情境 1:覆寫路徑前綴

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

前綴是擷取群組 {path} 之前的路徑開頭。對於 Proxy,前綴是 /api,而對於伺服器,前綴是 /app1。在這種情況下,Proxy 可以傳送 X-Forwarded-Prefix: /api,讓原始前綴 /api 覆寫伺服器前綴 /app1

情境 2:移除路徑前綴

有時,應用程式可能希望移除前綴。例如,請考慮以下 Proxy 到伺服器的對應

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

Proxy 沒有前綴,而應用程式 app1app2 分別具有路徑前綴 /app1/app2。Proxy 可以傳送 X-Forwarded-Prefix: ,讓空前綴覆寫伺服器前綴 /app1/app2

此部署情境的常見情況是,許可證是按生產應用程式伺服器付費的,並且最好在每個伺服器上部署多個應用程式以減少費用。另一個原因是為了在同一伺服器上執行更多應用程式,以便共用伺服器執行所需的資源。

在這些情境中,應用程式需要非空的 Context Root,因為同一伺服器上有多個應用程式。但是,這不應在公用 API 的 URL 路徑中可見,在公用 API 中,應用程式可以使用不同的子網域,從而提供諸如以下優點:

  • 新增安全性,例如同源政策

  • 應用程式的獨立擴展 (不同的網域指向不同的 IP 位址)

情境 3:插入路徑前綴

在其他情況下,可能需要預先加入前綴。例如,考慮以下 Proxy 到伺服器的對應

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

在此情況下,Proxy 的前綴為 /api/app1,而伺服器的前綴為 /app1。Proxy 可以傳送 X-Forwarded-Prefix: /api/app1,讓原始前綴 /api/app1 覆寫伺服器前綴 /app1

ForwardedHeaderTransformer

ForwardedHeaderTransformer 是一個組件,它會根據轉發的標頭修改請求的主機、埠和 scheme,然後移除這些標頭。如果您將其宣告為名稱為 forwardedHeaderTransformer 的 bean,它將會被 偵測到 並使用。

在 5.1 版本中,ForwardedHeaderFilter 已被棄用,並由 ForwardedHeaderTransformer 取代,以便可以更早地處理轉發的標頭,在建立交換之前。如果仍然配置了篩選器,則會將其從篩選器列表中移除,並改為使用 ForwardedHeaderTransformer

安全性考量

轉發的標頭存在安全性考量,因為應用程式無法知道標頭是由 Proxy 依預期加入,還是由惡意用戶端加入。這就是為什麼信任邊界上的 Proxy 應配置為移除來自外部不受信任的轉發流量。您也可以將 ForwardedHeaderTransformer 配置為 removeOnly=true,在這種情況下,它只會移除標頭,而不會使用它們。

篩選器

WebHandler API 中,您可以使用 WebFilter 在篩選器和目標 WebHandler 的其餘處理鏈之前和之後套用攔截樣式的邏輯。當使用 WebFlux Config 時,註冊 WebFilter 就像將其宣告為 Spring bean 一樣簡單,並且 (選擇性地) 可以透過在 bean 宣告上使用 @Order 或實作 Ordered 來表達優先順序。

CORS

Spring WebFlux 透過控制器上的註解,為 CORS 配置提供細緻的支援。但是,當您將其與 Spring Security 一起使用時,我們建議依賴內建的 CorsFilter,它必須排在 Spring Security 的篩選器鏈之前。

請參閱關於 CORSCORS WebFilter 的章節,以取得更多詳細資訊。

URL 處理器

您可能希望您的控制器端點比對 URL 路徑中帶有或不帶有尾部斜線的路徑。例如,"GET /home" 和 "GET /home/" 都應由使用 @GetMapping("/home") 註解的控制器方法處理。

將尾部斜線變體新增至所有對應宣告並非處理此用例的最佳方式。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 API 中,您可以使用 WebExceptionHandler 來處理來自 WebFilter 實例鏈和目標 WebHandler 的例外。當使用 WebFlux Config 時,註冊 WebExceptionHandler 就像將其宣告為 Spring bean 一樣簡單,並且 (選擇性地) 可以透過在 bean 宣告上使用 @Order 或實作 Ordered 來表達優先順序。

下表描述了可用的 WebExceptionHandler 實作

例外處理器 描述

ResponseStatusExceptionHandler

透過將回應設定為例外的 HTTP 狀態碼,為 ResponseStatusException 類型的例外提供處理。

WebFluxResponseStatusExceptionHandler

ResponseStatusExceptionHandler 的擴充功能,它還可以判斷任何例外上 @ResponseStatus 註解的 HTTP 狀態碼。

此處理器在 WebFlux Config 中宣告。

編解碼器

spring-webspring-core 模組透過使用 Reactive Streams 背壓的非阻塞 I/O,提供將位元組內容序列化和反序列化為更高等級物件的支援。以下描述了此支援

  • EncoderDecoder 是低階協定,用於編碼和解碼獨立於 HTTP 的內容。

  • HttpMessageReaderHttpMessageWriter 是用於編碼和解碼 HTTP 訊息內容的協定。

  • Encoder 可以使用 EncoderHttpMessageWriter 包裝,以使其適用於 Web 應用程式,而 Decoder 可以使用 DecoderHttpMessageReader 包裝。

  • DataBuffer 抽象化了不同的位元組緩衝區表示形式 (例如,Netty ByteBufjava.nio.ByteBuffer 等),並且是所有編解碼器運作的基礎。請參閱「Spring Core」章節中的 資料緩衝區和編解碼器,以瞭解有關此主題的更多資訊。

spring-core 模組提供 byte[]ByteBufferDataBufferResourceString 編碼器和解碼器實作。spring-web 模組提供 Jackson JSON、Jackson Smile、JAXB2、Protocol Buffers 和其他編碼器和解碼器,以及用於表單資料、多部分內容、伺服器發送事件等的僅限 Web 的 HTTP 訊息讀取器和寫入器實作。

ClientCodecConfigurerServerCodecConfigurer 通常用於配置和自訂應用程式中使用的編解碼器。請參閱關於配置 HTTP 訊息編解碼器 的章節。

Jackson JSON

當 Jackson 程式庫存在時,JSON 和二進位 JSON (Smile) 都受到支援。

Jackson2Decoder 的運作方式如下

  • Jackson 的非同步、非阻塞剖析器用於將位元組區塊串流聚合到 TokenBuffer 中,每個 TokenBuffer 代表一個 JSON 物件。

  • 每個 TokenBuffer 都會傳遞到 Jackson 的 ObjectMapper,以建立更高等級的物件。

  • 當解碼為單值發布者 (例如,Mono) 時,會有一個 TokenBuffer

  • 當解碼為多值發布者 (例如,Flux) 時,一旦接收到足夠的位元組以形成完整的物件,每個 TokenBuffer 都會傳遞到 ObjectMapper。輸入內容可以是 JSON 陣列,或任何 行分隔 JSON 格式,例如 NDJSON、JSON Lines 或 JSON Text Sequences。

Jackson2Encoder 的運作方式如下

  • 對於單值發布者 (例如,Mono),只需透過 ObjectMapper 序列化它即可。

  • 對於具有 application/json 的多值發布者,預設情況下,使用 Flux#collectToList() 收集值,然後序列化產生的集合。

  • 對於具有串流媒體類型 (例如 application/x-ndjsonapplication/stream+x-jackson-smile) 的多值發布者,使用 行分隔 JSON 格式個別編碼、寫入和刷新每個值。其他串流媒體類型可以向編碼器註冊。

  • 對於 SSE,Jackson2Encoder 會針對每個事件調用,並且輸出會被刷新以確保無延遲交付。

預設情況下,Jackson2EncoderJackson2Decoder 都不支援 String 類型的元素。相反地,預設假設是字串或字串序列代表序列化的 JSON 內容,將由 CharSequenceEncoder 呈現。如果您需要的是從 Flux<String> 呈現 JSON 陣列,請使用 Flux#collectToList() 並編碼 Mono<List<String>>

表單資料

FormHttpMessageReaderFormHttpMessageWriter 支援解碼和編碼 application/x-www-form-urlencoded 內容。

在伺服器端,表單內容通常需要從多個位置存取,ServerWebExchange 提供專用的 getFormData() 方法,該方法透過 FormHttpMessageReader 剖析內容,然後快取結果以供重複存取。請參閱 表單資料WebHandler API 章節中。

一旦使用了 getFormData(),就無法再從請求正文中讀取原始原始內容。因此,預期應用程式會始終如一地透過 ServerWebExchange 來存取快取的表單資料,而不是從原始請求正文中讀取。

多部分

MultipartHttpMessageReaderMultipartHttpMessageWriter 支援解碼和編碼 "multipart/form-data"、"multipart/mixed" 和 "multipart/related" 內容。反過來,MultipartHttpMessageReader 委派給另一個 HttpMessageReader 進行實際剖析,以產生 Flux<Part>,然後簡單地將這些部分收集到 MultiValueMap 中。預設情況下,使用 DefaultPartHttpMessageReader,但可以透過 ServerCodecConfigurer 變更此設定。有關 DefaultPartHttpMessageReader 的更多資訊,請參閱 DefaultPartHttpMessageReader 的 javadoc

在伺服器端,多部分表單內容可能需要從多個位置存取,ServerWebExchange 提供專用的 getMultipartData() 方法,該方法透過 MultipartHttpMessageReader 剖析內容,然後快取結果以供重複存取。請參閱 多部分資料WebHandler API 章節中。

一旦使用了 getMultipartData(),就無法再從請求正文中讀取原始原始內容。因此,應用程式必須始終如一地使用 getMultipartData() 進行重複的、類似地圖的部分存取,否則依賴 SynchronossPartHttpMessageReader 進行一次性存取 Flux<Part>

Protocol Buffers

ProtobufEncoderProtobufDecoder 支援解碼和編碼 com.google.protobuf.Message 類型的 "application/x-protobuf"、"application/octet-stream" 和 "application/vnd.google.protobuf" 內容。如果使用帶有內容類型 (例如 "application/x-protobuf;delimited=true") 的 "delimited" 參數接收/傳送內容,它們也支援值串流。這需要 "com.google.protobuf:protobuf-java" 程式庫,版本 3.29 及更高版本。

ProtobufJsonDecoderProtobufJsonEncoder 變體支援讀取和寫入 JSON 文件到 Protobuf 訊息以及從 Protobuf 訊息讀取和寫入 JSON 文件。它們需要 "com.google.protobuf:protobuf-java-util" 相依性。請注意,JSON 變體不支援讀取訊息串流,請參閱 ProtobufJsonDecoder 的 javadoc 以取得更多詳細資訊。

限制

可以為緩衝部分或全部輸入串流的 DecoderHttpMessageReader 實作配置記憶體中要緩衝的最大位元組數量的限制。在某些情況下,會發生緩衝,因為輸入被聚合並表示為單個物件 — 例如,具有 @RequestBody byte[]x-www-form-urlencoded 資料等的控制器方法。當分割輸入串流時,也可能發生緩衝 — 例如,分隔文字、JSON 物件串流等。對於這些串流案例,限制適用於與串流中的一個物件關聯的位元組數。

要配置緩衝區大小,您可以檢查給定的 DecoderHttpMessageReader 是否公開 maxInMemorySize 屬性,如果公開,則 Javadoc 將包含有關預設值的詳細資訊。在伺服器端,ServerCodecConfigurer 提供了一個單一位置,可以從該位置設定所有編解碼器,請參閱 HTTP 訊息編解碼器。在用戶端,可以在 WebClient.Builder 中變更所有編解碼器的限制。

對於 多部分剖析maxInMemorySize 屬性限制了非檔案部分的大小。對於檔案部分,它決定了將部分寫入磁碟的閾值。對於寫入磁碟的檔案部分,還有一個額外的 maxDiskUsagePerPart 屬性來限制每個部分的磁碟空間量。還有一個 maxParts 屬性來限制多部分請求中的總部分數。要在 WebFlux 中配置所有三個屬性,您需要向 ServerCodecConfigurer 提供預先配置的 MultipartHttpMessageReader 實例。

串流

當串流到 HTTP 回應 (例如,text/event-streamapplication/x-ndjson) 時,定期傳送資料非常重要,以便更早而非更晚地可靠地偵測到已斷線的用戶端。此類傳送可以是僅註解的、空的 SSE 事件或任何其他「無操作」資料,這些資料可以有效地充當心跳訊號。

DataBuffer

DataBuffer 是 WebFlux 中位元組緩衝區的表示形式。本參考的 Spring Core 部分在關於 資料緩衝區和編解碼器 的章節中有更多關於此的資訊。要理解的重點是,在某些伺服器 (如 Netty) 上,位元組緩衝區是池化的和參考計數的,並且在使用時必須釋放,以避免記憶體洩漏。

WebFlux 應用程式通常不需要擔心這些問題,除非它們直接使用或產生資料緩衝區,而不是依賴編解碼器來轉換為更高等級的物件和從更高等級的物件轉換,或者除非它們選擇建立自訂編解碼器。對於這種情況,請查看 資料緩衝區和編解碼器 中的資訊,特別是關於 使用 DataBuffer 的章節。

記錄

Spring WebFlux 中的 DEBUG 層級記錄旨在簡潔、最小且人性化。它側重於高價值的資訊位元,這些資訊位元反覆有用,而不是僅在偵錯特定問題時有用的其他資訊位元。

TRACE 層級記錄通常遵循與 DEBUG 相同的原則 (例如,也不應是大量資訊),但可用於偵錯任何問題。此外,某些記錄訊息在 TRACEDEBUG 下可能會顯示不同的詳細程度。

良好的記錄來自使用記錄的經驗。如果您發現任何不符合既定目標的內容,請告知我們。

記錄 ID

在 WebFlux 中,單個請求可以在多個執行緒上執行,並且執行緒 ID 對於關聯屬於特定請求的記錄訊息沒有用。這就是為什麼預設情況下 WebFlux 記錄訊息以請求特定的 ID 作為前綴。

在伺服器端,記錄 ID 儲存在 ServerWebExchange 屬性 (LOG_ID_ATTRIBUTE) 中,而基於該 ID 的完全格式化的前綴可從 ServerWebExchange#getLogPrefix() 取得。在 WebClient 端,記錄 ID 儲存在 ClientRequest 屬性 (LOG_ID_ATTRIBUTE) 中,而完全格式化的前綴可從 ClientRequest#logPrefix() 取得。

敏感資料

DEBUGTRACE 記錄可以記錄敏感資訊。這就是為什麼預設情況下表單參數和標頭被遮罩,您必須明確啟用它們的完整記錄。

以下範例示範如何針對伺服器端請求執行此操作

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