Spring框架為調(diào)用REST端點提供了以下選擇:
- WebClient?- 非阻塞、響應(yīng)式客戶端和 fluent API。
- RestTemplate?- 帶有模板方法API的同步客戶端。
- HTTP 接口?- 注解式接口,并生成動態(tài)代理實現(xiàn)。
一、?WebClient
WebClient
?是一個非阻塞的、響應(yīng)式的客戶端,用于執(zhí)行HTTP請求。它在5.0中引入,提供了?RestTemplate
?的替代方案,支持同步、異步和流式場景。
WebClient
?支持以下特性:
-
非阻塞 I/O。
-
Reactive Streams 背壓。
-
以較少的硬件資源實現(xiàn)高并發(fā)性。
-
函數(shù)式、fluent API,利用了Java 8 lambdas的優(yōu)勢。
-
同步和異步互動。
-
服務(wù)器的流式上傳和下載。
詳情見WebClient 原理及實踐—官方原版
二、RestTemplate
RestTemplate
?提供了一個比HTTP客戶端庫更高層次的API。它使得在一行中調(diào)用REST端點變得容易。它暴露了以下幾組重載方法:?
方法組 | 說明 |
---|---|
|
通過 GET 檢索一個表示結(jié)果。 |
|
通過使用GET檢索一個? |
|
通過使用? |
|
通過使用 POST 創(chuàng)建一個新的資源,并從響應(yīng)中返回? |
|
通過使用POST創(chuàng)建一個新資源,并從響應(yīng)中返回表示。 |
|
通過使用POST創(chuàng)建一個新資源,并從響應(yīng)中返回表示。 |
|
通過使用PUT創(chuàng)建或更新一個資源。 |
|
通過使用 PATCH 更新一個資源,并從響應(yīng)中返回表示。請注意,JDK的? |
|
通過使用 DELETE 刪除指定URI上的資源。 |
|
通過使用 ALLOW 為資源檢索允許的HTTP方法。 |
|
前面的方法的更通用(和更少的意見)版本,在需要時提供額外的靈活性。它接受一個? 這些方法允許使用? |
|
執(zhí)行請求的最通用方式,通過回調(diào)接口對請求準備和響應(yīng)提取進行完全控制。 |
1、初始化
默認構(gòu)造函數(shù)使用?java.net.HttpURLConnection
?來執(zhí)行請求。你可以通過?ClientHttpRequestFactory
?的實現(xiàn)切換到不同的HTTP庫。有內(nèi)置的對以下內(nèi)容的支持:
-
Apache HttpComponents
-
Netty
-
OkHttp
例如,要切換 到Apache HttpComponents,你可以使用以下方法:
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
每個?ClientHttpRequestFactory
?都公開了底層HTTP客戶端庫的特定配置選項—?例如,對于憑證、連接池和其他細節(jié)。
URI
許多?RestTemplate
?方法接受URI模板和URI模板變量,或者作為一個?String
?變量參數(shù),或者作為?Map<String,String>
。
下面的例子使用了一個?String
?變量的參數(shù):
String result = restTemplate.getForObject(
"https://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");
下面的例子使用一個?Map<String, String>
:
Map<String, String> vars = Collections.singletonMap("hotel", "42");
String result = restTemplate.getForObject(
"https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
請記住URI模板是自動編碼的,如下例所示:
restTemplate.getForObject("https://example.com/hotel list", String.class);
// Results in request to "https://example.com/hotel%20list"
你可以使用?RestTemplate
?的?uriTemplateHandler
?屬性來定制URI的編碼方式。或者,你可以準備一個?java.net.URI
,并把它傳入接受?URI
?的?RestTemplate
?方法之一。
Header
你可以使用?exchange()
?方法來指定 header,如下例所示:
String uriTemplate = "https://example.com/hotels/{hotel}";
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).build(42);
RequestEntity<Void> requestEntity = RequestEntity.get(uri)
.header("MyRequestHeader", "MyValue")
.build();
ResponseEntity<String> response = template.exchange(requestEntity, String.class);
String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();
你可以通過許多返回?ResponseEntity
?的?RestTemplate
?方法變體獲得響應(yīng)頭信息。
2、Body
在?HttpMessageConverter
?的幫助下,傳入?RestTemplate
?方法和從?RestTemplate
?方法返回的對象被轉(zhuǎn)換為原始內(nèi)容。
在POST中,一個輸入對象被序列化到請求體中,如下面的例子所示:
URI location = template.postForLocation("https://example.com/people", person);
你不需要明確地設(shè)置請求的?Content-Type
?頭。在大多數(shù)情況下,你可以根據(jù)源對象類型找到一個兼容的消息轉(zhuǎn)換器(message converter),所選擇的消息轉(zhuǎn)換器會相應(yīng)地設(shè)置 content type。如果有必要,你可以使用?exchange
?方法來明確地提供?Content-Type
?的請求頭,而這又會影響到選擇何種消息轉(zhuǎn)換器。
在一個GET中,響應(yīng)的 body 被反序列化為一個輸出?Object
,如下例所示:
Person person = restTemplate.getForObject("https://example.com/people/{id}", Person.class, 42);
請求的 Accept 標頭不需要明確設(shè)置。在大多數(shù)情況下,可以根據(jù)預(yù)期的響應(yīng)類型找到一個兼容的消息轉(zhuǎn)換器,然后幫助填充 Accept 頭。如果有必要,你可以使用 exchange 方法來明確提供 Accept 頭。 默認情況下,RestTemplate 注冊了所有內(nèi)置的 消息轉(zhuǎn)換器(message converter),這取決于 classpath 檢查,有助于確定有哪些可選的 converter 庫存在。你也可以明確地設(shè)置要使用的消息轉(zhuǎn)換器。
消息轉(zhuǎn)換
spring-web
?模塊包含?HttpMessageConverter
?contract,用于通過?InputStream
?和?OutputStream
?讀寫 HTTP 請求和響應(yīng)的 body。HttpMessageConverter
?實例在客戶端(例如,在?RestTemplate
?中)和服務(wù)器端(例如,在Spring MVC REST controller 中)使用。
框架中提供了主要 type(MIME)type 的具體實現(xiàn),默認情況下,在客戶端與?RestTemplate
?注冊,在服務(wù)器端與?RequestMappingHandlerAdapter
?注冊
HttpMessageConverter
?的實現(xiàn)將在下面幾節(jié)中描述。對于所有的轉(zhuǎn)換器,都使用默認的 media type,但是你可以通過設(shè)置?supportedMediaTypes
?bean 屬性來覆蓋它。下表描述了每個實現(xiàn):
MessageConverter | 說明 |
---|---|
|
一個? |
|
一個? |
|
一個? |
|
一個? |
|
一個? |
|
一個? |
|
一個? |
|
一個? |
3、Jackson JSON 視圖
你可以指定一個 Jackson JSON 視圖 來只序列化對象屬性的一個子集,如下面的例子所示:
MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23"));
value.setSerializationView(User.WithoutPasswordView.class);
RequestEntity<MappingJacksonValue> requestEntity =
RequestEntity.post(new URI("https://example.com/user")).body(value);
ResponseEntity<String> response = template.exchange(requestEntity, String.class);
4、Multipart
為了發(fā)送 multipart 數(shù)據(jù),你需要提供一個?MultiValueMap<String, Object>
,其值可以是一個用于 part 內(nèi)容的?Object
,一個用于文件 part 的?Resource
,或者一個用于帶有 header 的 part 內(nèi)容的?HttpEntity
。比如說:
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
parts.add("fieldPart", "fieldValue");
parts.add("filePart", new FileSystemResource("...logo.png"));
parts.add("jsonPart", new Person("Jason"));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
parts.add("xmlPart", new HttpEntity<>(myBean, headers));
在大多數(shù)情況下,你不需要為每個 part 指定?Content-Type
。content type 是根據(jù)為序列化它而選擇的?HttpMessageConverter
?自動確定的,或者在?Resource
?的情況下,根據(jù)文件擴展名確定。如果有必要,你可以用一個?HttpEntity
?wrapper 明確地提供?MediaType
。
一旦?MultiValueMap
?準備好了,你就可以把它傳遞給?RestTemplate
,如下所示:
MultiValueMap<String, Object> parts = ...;
template.postForObject("https://example.com/upload", parts, Void.class);
如果?MultiValueMap
?至少包含一個非?String
?值,Content-Type
?就被?FormHttpMessageConverter
?設(shè)置為?multipart/form-data
。如果?MultiValueMap
?有?String
?值,Content-Type
?被默認為?application/x-www-form-urlencoded
。如果有必要,也可以明確設(shè)置?Content-Type
。
三、HTTP 接口
Spring框架允許你將HTTP服務(wù)定義為一個Java接口,并為 HTTP exchange 提供注解方法。然后你可以生成一個實現(xiàn)該接口并執(zhí)行 exchange 的代理。這有助于簡化HTTP遠程訪問,因為遠程訪問通常涉及到一個 facade,該 facade 包裝了使用底層HTTP客戶端的細節(jié)。
聲明一個帶有?@HttpExchange
?方法的接口:
interface RepositoryService {
@GetExchange("/repos/{owner}/{repo}")
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
// more HTTP exchange methods...
}
創(chuàng)建一個代理,執(zhí)行所聲明的 HTTP exchange:
WebClient client = WebClient.builder().baseUrl("https://api.github.com/").build();
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
RepositoryService service = factory.createClient(RepositoryService.class);
在類型層面上支持?@HttpExchange
,它適用于所有方法:
@HttpExchange(url = "/repos/{owner}/{repo}", accept = "application/vnd.github.v3+json")
interface RepositoryService {
@GetExchange
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
@PatchExchange(contentType = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
void updateRepository(@PathVariable String owner, @PathVariable String repo,
@RequestParam String name, @RequestParam String description, @RequestParam String homepage);
}
1、方法參數(shù)
注解式 HTTP exchange 方法支持靈活的方法簽名,有以下方法參數(shù):
方法參數(shù) | 說明 |
---|---|
|
動態(tài)設(shè)置請求的URL,覆蓋注解的? |
|
動態(tài)地設(shè)置請求的HTTP方法,覆蓋注解的? |
|
添加一個請求頭或多個頭。參數(shù)可以是一個? |
|
添加一個變量,用于在請求URL中擴展一個占位符。參數(shù)可以是一個帶有多個變量的? |
|
提供請求的 body,可以是要被序列化的對象,也可以是Reactive Streams? |
|
添加一個請求參數(shù)或多個參數(shù)。參數(shù)可以是一個? 當? |
|
添加一個 request part,它可以是一個? |
|
添加一個或多個cookie。參數(shù)可以是一個? |
2、返回值
注解式 HTTP exchange 方法支持以下返回值:
方法返回值 | 說明 |
---|---|
|
執(zhí)行給定的請求,并釋放響應(yīng)內(nèi)容,如果有的話。 |
|
執(zhí)行給定的請求,釋放響應(yīng)內(nèi)容(如果有),并返回響應(yīng)頭信息。 |
|
執(zhí)行給定的請求并將響應(yīng)內(nèi)容解碼為聲明的返回類型。 |
|
執(zhí)行給定的請求,并將響應(yīng)內(nèi)容解碼為聲明的元素類型的流。 |
|
執(zhí)行給定的請求,并釋放響應(yīng)內(nèi)容(如果有的話),并返回一個帶有狀態(tài)和頭信息的? |
|
執(zhí)行給定的請求,將響應(yīng)內(nèi)容解碼為聲明的返回類型,并返回一個帶有狀態(tài)、頭信息和解碼后的 body 的? |
|
執(zhí)行給定的請求,將響應(yīng)內(nèi)容解碼為聲明的元素類型的流,并返回一個帶有狀態(tài)、頭信息和解碼后的響應(yīng)體流的? |
3、異常處理
默認情況下,WebClient
?會對 4xx 和 5xx HTTP狀態(tài)代碼引發(fā)?WebClientResponseException
。要定制這一點,你可以注冊一個響應(yīng)狀態(tài) handler,適用于通過客戶端執(zhí)行的所有響應(yīng):
WebClient webClient = WebClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
.build();
WebClientAdapter clientAdapter = WebClientAdapter.forClient(webClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory
.builder(clientAdapter).build();
關(guān)于更多的細節(jié)和選項,例如抑制錯誤狀態(tài)代碼,請參見?WebClient.Builder
?中?defaultStatusHandler
?的 Javadoc。文章來源:http://www.zghlxwxcb.cn/news/detail-728874.html
大家好,我是Doker品牌的Sinbad,歡迎點贊和評論,您的鼓勵是我們持續(xù)更新的動力!歡迎加微信進入技術(shù)群聊!文章來源地址http://www.zghlxwxcb.cn/news/detail-728874.html
到了這里,關(guān)于Spring 教程—REST 客戶端詳解(WebClient 、RestTemplate、HTTP 接口)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!