Spring 5 WebClient
1.概述
在本教程中,我們將研究WebClient
,它是Spring 5中引入的反應式Web客戶端。
我們還將看一下WebTestClient,
是一個旨在用於測試的WebClient
。
2.什麼是WebClient
?
簡而言之, WebClient
是代表執行Web請求的主要入口點的接口。
它是作為Spring Web Reactive模塊的一部分創建的, RestTemplate
在這些情況下替代經典的RestTemplate
。此外,新客戶端是一種反應式,無阻塞的解決方案,可通過HTTP / 1.1協議工作。
最後,該接口具有一個實現,即我們將使用的DefaultWebClient
類。
3.依存關係
由於我們使用的是Spring Boot應用程序,因此我們需要spring-boot-starter-webflux
依賴項以及Reactor項目。
3.1。用Maven構建
讓我們將以下依賴項添加到pom.xml
文件中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectreactor</groupId>
<artifactId>reactor-spring</artifactId>
<version>1.0.1.RELEASE</version>
</dependency>
3.2。用Gradle構建
使用Gradle,我們需要將以下條目添加到build.gradle
文件中:
dependencies {
compile 'org.springframework.boot:spring-boot-starter-webflux'
compile 'org.projectreactor:reactor-spring:1.0.1.RELEASE'
}
4.使用WebClient
為了與客戶正常合作,我們需要知道如何:
- 創建一個實例
- 發出請求
- 處理回應
4.1。創建一個WebClient
實例
有三個選項可供選擇。第一個是使用默認設置創建一個WebClient對象:
WebClient client1 = WebClient.create();
第二種選擇是使用給定的基本URI初始化WebClient實例:
WebClient client2 = WebClient.create("http://localhost:8080");
第三個選項(也是最高級的一個)是使用DefaultWebClientBuilder類構建客戶端,該類允許完全自定義:
WebClient client3 = WebClient
.builder()
.baseUrl("http://localhost:8080")
.defaultCookie("cookieKey", "cookieValue")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080"))
.build();
4.2。創建具有超時的WebClient
實例
通常,默認的30秒HTTP超時對於我們的需求來說太慢了,因此讓我們看看如何為WebClient
實例配置它們。
我們使用的核心類是TcpClient.
在這裡,我們可以通過ChannelOption.CONNECT_TIMEOUT_MILLIS
值設置連接超時。我們還可以分別使用ReadTimeoutHandler
和WriteTimeoutHandler
設置讀取和寫入超時:
TcpClient tcpClient = TcpClient
.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.doOnConnected(connection -> {
connection.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS));
connection.addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS));
});
WebClient client = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)))
.build();
請注意,雖然我們也可以在客戶端請求上調用timeout
,但這是信號超時,而不是HTTP連接或讀/寫超時; Mono / Flux發布者超時。
4.3 準備請求
首先,我們需要通過調用method(HttpMethod method)或調用其快捷方式(例如get
, post和delete )來指定請求的HTTP方法:
WebClient.UriSpec<WebClient.RequestBodySpec> request1 = client3.method(HttpMethod.POST);
WebClient.UriSpec<WebClient.RequestBodySpec> request2 = client3.post();
下一步是提供URL。我們可以將它作為String或java.net.URL
實例傳遞給uri API:
WebClient.RequestBodySpec uri1 = client3
.method(HttpMethod.POST)
.uri("/resource");
WebClient.RequestBodySpec uri2 = client3
.post()
.uri(URI.create("/resource"));
然後,我們可以根據需要設置請求正文,內容類型,長度,cookie或標頭。
例如,如果我們要設置一個請求主體,有兩種可用的方法:用BodyInserter
填充請求主體或將此工作委託給Publisher
:
WebClient.RequestHeadersSpec requestSpec1 = WebClient
.create()
.method(HttpMethod.POST)
.uri("/resource")
.body(BodyInserters.fromPublisher(Mono.just("data")), String.class);
WebClient.RequestHeadersSpec<?> requestSpec2 = WebClient
.create("http://localhost:8080")
.post()
.uri(URI.create("/resource"))
.body(BodyInserters.fromObject("data"));
BodyInserter是一個接口,負責用給定的輸出消息和插入期間使用的上下文填充ReactiveHttpOutputMessage主體。 Publisher
是一種反應性組件,負責提供可能無限數量的已排序元素。
第二種方法是body
方法,它是原始body(BodyInserter inserter)
方法的快捷方式。
為了減輕BodyInserter,
的填充過程, BodyInserters
類具有許多有用的實用程序方法:
BodyInserter<Publisher<String>, ReactiveHttpOutputMessage> inserter1 = BodyInserters
.fromPublisher(Subscriber::onComplete, String.class);
MultiValueMap也可以:
LinkedMultiValueMap map = new LinkedMultiValueMap();
map.add("key1", "value1");
map.add("key2", "value2");
BodyInserter<MultiValueMap, ClientHttpRequest> inserter2
= BodyInserters.fromMultipartData(map);
或通過使用單個對象:
BodyInserter<Object, ReactiveHttpOutputMessage> inserter3
= BodyInserters.fromObject(new Object());
設置正文之後,我們可以設置標題,cookie和可接受的媒體類型。實例化客戶端時,會將值添加到已設置的值中。
此外,還對最常用的標頭提供了額外的支持,例如“If-None-Match”, “If-Modified-Since”, “Accept”,
和“Accept-Charset”.
這是如何使用這些值的示例:
WebClient.ResponseSpec response1 = uri1
.body(inserter3)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
.acceptCharset(Charset.forName("UTF-8"))
.ifNoneMatch("*")
.ifModifiedSince(ZonedDateTime.now())
.retrieve();
4.4。得到回應
最後階段是發送請求並接收響應。這可以通過exchange
或retrieve
方法來完成。
這些方法的返回類型有所不同。 exchange
方法提供ClientResponse
以及其狀態和標頭,而retrieve
方法是直接獲取主體的最短路徑:
String response2 = request1.exchange()
.block()
.bodyToMono(String.class)
.block();
String response3 = request2
.retrieve()
.bodyToMono(String.class)
.block();
重要的是要注意bodyToMono
方法,如果狀態代碼是4xx (客戶端錯誤)或5xx (服務器錯誤),它將拋出WebClientException 。我們在Mono
上使用block
方法來訂閱和檢索與響應一起發送的實際數據。
5.使用WebTestClient
WebTestClient是測試WebFlux服務器端點的主要入口點。它具有與WebClient非常相似的API,並且將大部分工作委託給內部WebClient實例,主要專注於提供測試上下文。 DefaultWebTestClient類是單個接口實現。
測試客戶端可以綁定到真實服務器,也可以使用特定的控制器或功能。
5.1。綁定到服務器
要完成對正在運行的服務器的實際請求的端到端集成測試,我們可以使用bindToServer方法:
WebTestClient testClient = WebTestClient
.bindToServer()
.baseUrl("http://localhost:8080")
.build();
5.2。綁定到路由
我們可以通過將特定的RouterFunction傳遞給bindToRouterFunction方法來測試它:
RouterFunction function = RouterFunctions.route(
RequestPredicates.GET("/resource"),
request -> ServerResponse.ok().build()
);
WebTestClient
.bindToRouterFunction(function)
.build().get().uri("/resource")
.exchange()
.expectStatus().isOk()
.expectBody().isEmpty();
5.3。綁定到Web處理程序
使用bindToWebHandler
方法可以實現相同的行為,該方法採用一個WebHandler
實例:
WebHandler handler = exchange -> Mono.empty();
WebTestClient.bindToWebHandler(handler).build();
5.4。綁定到應用程序上下文
當我們使用bindToApplicationContext
方法時,會發生一種更有趣的情況。它使用ApplicationContext
並分析控制器bean和@EnableWebFlux
配置的上下文。
如果我們注入ApplicationContext
的實例,則簡單的代碼片段可能如下所示:
@Autowired
private ApplicationContext context;
WebTestClient testClient = WebTestClient.bindToApplicationContext(context)
.build();
5.5。綁定到控制器
一種較短的方法是提供一個我們想通過bindToController
方法測試的控制器數組。假設我們有一個Controller類並將其註入到所需的類中,我們可以編寫:
@Autowired
private Controller controller;
WebTestClient testClient = WebTestClient.bindToController(controller).build();
5.6。發出請求
構建WebTestClient對象之後,鏈中的所有後續操作都將與WebClient相似,直到交換方法(一種獲得響應的方法)為止,該方法提供了WebTestClient.ResponseSpec接口以與諸如ExpectStatus , ExpectBody之類的有用方法一起使用和ExpectHeader :
WebTestClient
.bindToServer()
.baseUrl("http://localhost:8080")
.build()
.post()
.uri("/resource")
.exchange()
.expectStatus().isCreated()
.expectHeader().valueEquals("Content-Type", "application/json")
.expectBody().isEmpty();
六,結論
在本文中,我們探討了WebClient,
是一種新的增強型Spring機制,用於在客戶端發出請求。
我們還研究了它通過配置客戶端,準備請求和處理響應而提供的好處。
文章中提到的所有代碼片段都可以在我們的GitHub存儲庫中找到。