SpringCloud(1-8) WebFlux 的 ServerWebExchange


WebFlux 的 ServerWebExchange

ServerWebExchange 的注釋:

提示

ServerWebExchange 是『Spring Reactive Web 世界中』HTTP 請求與響應交互的契約。提供對 HTTP 請求和響應的訪問,並公開額外的服務器端處理相關屬性和特性,如請求屬性。

其實,ServerWebExchange 命名為『服務網絡交換器』,存放着重要的請求-響應屬性、請求實例和響應實例等等,有點像 Servlet 中的 Context 的角色。

public interface ServerWebExchange { // 日志前綴屬性的 KEY,值為 org.springframework.web.server.ServerWebExchange.LOG_ID // 可以理解為 attributes.set("org.springframework.web.server.ServerWebExchange.LOG_ID", "日志前綴的具體值"); // 作用是打印日志的時候會拼接這個 KEY 對應的前綴值,默認值為 "" String LOG_ID_ATTRIBUTE = ServerWebExchange.class.getName() + ".LOG_ID"; String getLogPrefix(); // 獲取 ServerHttpRequest 對象 ServerHttpRequest getRequest(); // 獲取 ServerHttpResponse 對象 ServerHttpResponse getResponse(); // 返回當前 exchange 的請求屬性,返回結果是一個可變的 Map Map<String, Object> getAttributes(); // 根據 KEY 獲取請求屬性 @Nullable default <T> T getAttribute(String name) { return (T) getAttributes().get(name); } // 根據 KEY 獲取請求屬性,做了非空判斷 @SuppressWarnings("unchecked") default <T> T getRequiredAttribute(String name) { T value = getAttribute(name); Assert.notNull(value, () -> "Required attribute '" + name + "' is missing"); return value; } // 根據 KEY 獲取請求屬性,需要提供默認值 @SuppressWarnings("unchecked") default <T> T getAttributeOrDefault(String name, T defaultValue) { return (T) getAttributes().getOrDefault(name, defaultValue); } // 返回當前請求的網絡會話 Mono<WebSession> getSession(); // 返回當前請求的認證用戶,如果存在的話 <T extends Principal> Mono<T> getPrincipal(); // 返回請求的表單數據或者一個空的 Map,只有 Content-Type為application/x-www-form-urlencoded 的時候這個方法才會返回一個非空的 Map -- 這個一般是表單數據提交用到 Mono<MultiValueMap<String, String>> getFormData(); // 返回 multipart 請求的 part 數據或者一個空的 Map,只有 Content-Type 為 multipart/form-data 的時候這個方法才會返回一個非空的 Map -- 這個一般是文件上傳用到 Mono<MultiValueMap<String, Part>> getMultipartData(); // 返回 Spring 的上下文 @Nullable ApplicationContext getApplicationContext(); // 這幾個方法和 lastModified 屬性相關 boolean isNotModified(); boolean checkNotModified(Instant lastModified); boolean checkNotModified(String etag); boolean checkNotModified(@Nullable String etag, Instant lastModified); // URL 轉換 String transformUrl(String url); // URL 轉換映射 void addUrlTransformer(Function<String, String> transformer); // 注意這個方法,方法名是:改變,這個是修改 ServerWebExchange 屬性的方法,返回的是一個 Builder 實例,Builder 是 ServerWebExchange 的內部類 default Builder mutate() { return new DefaultServerWebExchangeBuilder(this); } // ServerWebExchange 構造器 interface Builder { // 覆蓋 ServerHttpRequest Builder request(Consumer<ServerHttpRequest.Builder> requestBuilderConsumer); Builder request(ServerHttpRequest request); // 覆蓋 ServerHttpResponse Builder response(ServerHttpResponse response); // 覆蓋當前請求的認證用戶 Builder principal(Mono<Principal> principalMono); // 構建新的 ServerWebExchange 實例 ServerWebExchange build(); } } 
Copied!

注意到 ServerWebExchange#mutate 方法,ServerWebExchange 實例可以理解為不可變實例。

如果我們想要修改它,需要通過 ServerWebExchange#mutate 方法生成一個新的實例,后面會修改請求以及響應時會用到,暫時不做介紹。

#1. ServerWebExchange 對比 Servlet

這里總結部分我在寫代碼中遇到的一些不同與相應代替辦法

  • request.setAttribute(“key”, "value"); 的替代

    ServerHttpRequest 中並無 Attribute 相關操作,要通過 exchange 來操作。

    exchange.getAttributes().put(“key”, "value");

  • request.getHeader("test"); 的替代

    request.getHeaders().getFirst(“test”)

    遍歷 Header 如下:

    HttpHeaders headers = request.getHeaders(); for (Map.Entry<String,List<String>> header:headers.entrySet()) { 
    Copied!

String key = header.getKey();    List values = header.getValue(); } ```

#2. ServerHttpRequest

ServerHttpRequest 實例是用於承載請求相關的屬性和請求體,Spring Cloud Gateway 中底層使用 Netty 處理網絡請求。

通過追溯源碼,可以從 ReactorHttpHandlerAdapter 中得知 ServerWebExchange 實例中持有的 ServerHttpRequest 實例的具體實現是 ReactorServerHttpRequest 。

之所以列出這些實例之間的關系,是因為這樣比較容易理清一些隱含的問題,例如:ReactorServerHttpRequest 的父類 AbstractServerHttpRequest 中初始化內部屬性 headers 的時候把請求的 HTTP 頭部封裝為只讀的實例:

// HttpHeaders 類中的 readOnlyHttpHeaders 方法,其中 ReadOnlyHttpHeaders 屏蔽了所有修改請求頭的方法,直接拋出 UnsupportedOperationException public static HttpHeaders readOnlyHttpHeaders(HttpHeaders headers) { Assert.notNull(headers, "HttpHeaders must not be null"); if (headers instanceof ReadOnlyHttpHeaders) { return headers; } else { return new ReadOnlyHttpHeaders(headers); } } 
Copied!

所有接口:

ublic interface HttpMessage { // 獲取請求頭,目前的實現中返回的是 ReadOnlyHttpHeaders 實例,只讀 HttpHeaders getHeaders(); } public interface ReactiveHttpInputMessage extends HttpMessage { // 返回請求體的 Flux 封裝 Flux<DataBuffer> getBody(); } public interface HttpRequest extends HttpMessage { // 返回 HTTP 請求方法,解析為 HttpMethod 實例 @Nullable default HttpMethod getMethod() { return HttpMethod.resolve(getMethodValue()); } // 返回 HTTP 請求方法,字符串 String getMethodValue(); // 請求的 URI URI getURI(); } public interface ServerHttpRequest extends HttpRequest, ReactiveHttpInputMessage { // 連接的唯一標識或者用於日志處理標識 String getId(); // 獲取請求路徑,封裝為 RequestPath 對象 RequestPath getPath(); // 返回查詢參數,是只讀的 MultiValueMap 實例 MultiValueMap<String, String> getQueryParams(); // 返回 Cookie 集合,是只讀的 MultiValueMap 實例 MultiValueMap<String, HttpCookie> getCookies(); // 遠程服務器地址信息 @Nullable default InetSocketAddress getRemoteAddress() { return null; } // SSL 會話實現的相關信息 @Nullable default SslInfo getSslInfo() { return null; } // 修改請求的方法,返回一個建造器實例 Builder,Builder 是內部類 default ServerHttpRequest.Builder mutate() { return new DefaultServerHttpRequestBuilder(this); } interface Builder { // 覆蓋請求方法 Builder method(HttpMethod httpMethod); // 覆蓋請求的 URI、請求路徑或者上下文,這三者相互有制約關系,具體可以參考 API 注釋 Builder uri(URI uri); Builder path(String path); Builder contextPath(String contextPath); // 覆蓋請求頭 Builder header(String key, String value); Builder headers(Consumer<HttpHeaders> headersConsumer); // 覆蓋 SslInfo Builder sslInfo(SslInfo sslInfo); // 構建一個新的 ServerHttpRequest 實例 ServerHttpRequest build(); } } 
Copied!

#3. ServerHttpResponse

ServerHttpResponse 實例是用於承載響應相關的屬性和響應體,Spring Cloud Gateway 中底層使用 Netty 處理網絡請求。

通過追溯源碼,可以從 ReactorHttpHandlerAdapter 中得知 ServerWebExchange 實例中持有的 ServerHttpResponse 實例的具體實現是 ReactorServerHttpResponse 。

之所以列出這些實例之間的關系,是因為這樣比較容易理清一些隱含的問題,例如:ReactorServerHttpResponse 構造函數初始化實例的時候,存放響應 Header 的是 HttpHeaders 實例,也就是響應 Header 是可以直接修改的。

public ReactorServerHttpResponse(HttpServerResponse response, DataBufferFactory bufferFactory) { super(bufferFactory, new HttpHeaders(new NettyHeadersAdapter(response.responseHeaders()))); Assert.notNull(response, "HttpServerResponse must not be null"); this.response = response; } 
Copied!

5.1.所有接口

public interface HttpMessage { // 獲取響應 Header,目前的實現中返回的是 HttpHeaders 實例,可以直接修改 HttpHeaders getHeaders(); } public interface ReactiveHttpOutputMessage extends HttpMessage { // 獲取 DataBufferFactory 實例,用於包裝或者生成數據緩沖區 DataBuffer 實例(創建響應體) DataBufferFactory bufferFactory(); // 注冊一個動作,在 HttpOutputMessage 提交之前此動作會進行回調 void beforeCommit(Supplier<? extends Mono<Void>> action); // 判斷 HttpOutputMessage 是否已經提交 boolean isCommitted(); // 寫入消息體到 HTTP 協議層 Mono<Void> writeWith(Publisher<? extends DataBuffer> body); // 寫入消息體到 HTTP 協議層並且刷新緩沖區 Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body); // 指明消息處理已經結束,一般在消息處理結束自動調用此方法,多次調用不會產生副作用 Mono<Void> setComplete(); } public interface ServerHttpResponse extends ReactiveHttpOutputMessage { // 設置響應狀態碼 boolean setStatusCode(@Nullable HttpStatus status); // 獲取響應狀態碼 @Nullable HttpStatus getStatusCode(); // 獲取響應 Cookie,封裝為 MultiValueMap 實例,可以修改 MultiValueMap<String, ResponseCookie> getCookies(); // 添加響應 Cookie void addCookie(ResponseCookie cookie); } 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM