呼叫 REST 服務
Spring Boot 提供了多種便捷的方式來呼叫遠端 REST 服務。如果您正在開發非阻塞響應式應用程式並使用 Spring WebFlux,那麼您可以使用 WebClient。如果您喜歡命令式 API,那麼您可以使用 RestClient 或 RestTemplate。
WebClient
如果您的類路徑中包含 Spring WebFlux,我們建議您使用 WebClient 來呼叫遠端 REST 服務。WebClient 介面提供了一個函式式風格的 API,並且是完全響應式的。您可以在 Spring Framework 文件的專門章節中瞭解更多關於 WebClient 的資訊。
如果您沒有編寫響應式 Spring WebFlux 應用程式,則可以使用 RestClient 代替 WebClient。這提供了一個類似的函式式 API,但它是命令式的而不是響應式的。 |
Spring Boot 會為您建立並預配置一個原型 WebClient.Builder bean。強烈建議您將其注入到您的元件中,並使用它來建立 WebClient 例項。Spring Boot 會配置該構建器以共享 HTTP 資源並以與伺服器相同的方式反映編解碼器設定(參見WebFlux HTTP 編解碼器自動配置)等等。
以下程式碼顯示了一個典型示例
-
Java
-
Kotlin
import reactor.core.publisher.Mono;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
@Service
public class MyService {
private final WebClient webClient;
public MyService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.baseUrl("https://example.org").build();
}
public Mono<Details> someRestCall(String name) {
return this.webClient.get().uri("/{name}/details", name).retrieve().bodyToMono(Details.class);
}
}
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono
@Service
class MyService(webClientBuilder: WebClient.Builder) {
private val webClient: WebClient
init {
webClient = webClientBuilder.baseUrl("https://example.org").build()
}
fun someRestCall(name: String): Mono<Details> {
return webClient.get().uri("/{name}/details", name)
.retrieve().bodyToMono(Details::class.java)
}
}
WebClient 執行時
Spring Boot 將根據應用程式類路徑中可用的庫自動檢測用於驅動 WebClient 的 ClientHttpConnector。按優先順序順序,支援以下客戶端
-
Reactor Netty
-
Jetty RS 客戶端
-
Apache HttpClient
-
JDK HttpClient
如果類路徑上有多個客戶端可用,將使用優先順序最高的客戶端。
預設情況下,spring-boot-starter-webflux 啟動器依賴於 io.projectreactor.netty:reactor-netty,它同時提供了伺服器和客戶端實現。如果您選擇使用 Jetty 作為響應式伺服器,則應新增對 Jetty Reactive HTTP 客戶端庫 org.eclipse.jetty:jetty-reactive-httpclient 的依賴。為伺服器和客戶端使用相同的技術具有其優勢,因為它將自動在客戶端和伺服器之間共享 HTTP 資源。
開發人員可以透過提供自定義的 ReactorResourceFactory 或 JettyResourceFactory bean 來覆蓋 Jetty 和 Reactor Netty 的資源配置——這將同時應用於客戶端和伺服器。
如果您希望為客戶端覆蓋此選擇,您可以定義自己的 ClientHttpConnector bean,並完全控制客戶端配置。
您可以在 Spring Framework 參考文件中瞭解更多關於 WebClient 配置選項的資訊。
全域性 HTTP 聯結器配置
如果自動檢測到的 ClientHttpConnector 不滿足您的需求,您可以使用 spring.http.clients.reactive.connector 屬性來選擇特定的聯結器。例如,如果您的類路徑中有 Reactor Netty,但您更喜歡 Jetty 的 HttpClient,您可以新增以下內容
-
屬性
-
YAML
spring.http.clients.reactive.connector=jetty
spring:
http:
clients:
reactive:
connector: jetty
| 您還可以使用適用於所有 HTTP 客戶端的全域性配置屬性。 |
對於更復雜的自定義,您可以使用 ClientHttpConnectorBuilderCustomizer 或宣告您自己的 ClientHttpConnectorBuilder bean,這將導致自動配置回退。當您需要自定義底層 HTTP 庫的一些內部實現時,這會很有用。
例如,以下程式碼將使用配置了特定 ProxySelector 的 JDK 客戶端
-
Java
-
Kotlin
import java.net.ProxySelector;
import org.springframework.boot.http.client.reactive.ClientHttpConnectorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyConnectorHttpConfiguration {
@Bean
ClientHttpConnectorBuilder<?> clientHttpConnectorBuilder(ProxySelector proxySelector) {
return ClientHttpConnectorBuilder.jdk().withHttpClientCustomizer((builder) -> builder.proxy(proxySelector));
}
}
import org.springframework.boot.http.client.reactive.ClientHttpConnectorBuilder;
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.net.ProxySelector
@Configuration(proxyBeanMethods = false)
class MyConnectorHttpConfiguration {
@Bean
fun clientHttpConnectorBuilder(proxySelector: ProxySelector): ClientHttpConnectorBuilder<*> {
return ClientHttpConnectorBuilder.jdk().withHttpClientCustomizer { builder -> builder.proxy(proxySelector) }
}
}
WebClient 自定義
根據您希望自定義應用的範圍,有三種主要的 WebClient 自定義方法。
為了使任何自定義的範圍儘可能窄,請注入自動配置的 WebClient.Builder,然後根據需要呼叫其方法。WebClient.Builder 例項是有狀態的:對構建器的任何更改都會反映在使用它隨後建立的所有客戶端中。如果您想使用相同的構建器建立多個客戶端,您也可以考慮使用 WebClient.Builder other = builder.clone(); 克隆構建器。
若要對所有 WebClient.Builder 例項進行應用程式範圍的累加自定義,您可以宣告 WebClientCustomizer bean 並在注入點區域性更改 WebClient.Builder。
最後,您可以回退到原始 API 並使用 WebClient.create()。在這種情況下,不會應用任何自動配置或 WebClientCustomizer。
WebClient SSL 支援
如果您需要在 WebClient 使用的 ClientHttpConnector 上進行自定義 SSL 配置,您可以注入一個 WebClientSsl 例項,該例項可以透過構建器的 apply 方法使用。
WebClientSsl 介面提供了對您在 application.properties 或 application.yaml 檔案中定義的任何 SSL 捆綁包的訪問。
以下程式碼顯示了一個典型示例
-
Java
-
Kotlin
import reactor.core.publisher.Mono;
import org.springframework.boot.webclient.autoconfigure.WebClientSsl;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
@Service
public class MyService {
private final WebClient webClient;
public MyService(WebClient.Builder webClientBuilder, WebClientSsl ssl) {
this.webClient = webClientBuilder.baseUrl("https://example.org").apply(ssl.fromBundle("mybundle")).build();
}
public Mono<Details> someRestCall(String name) {
return this.webClient.get().uri("/{name}/details", name).retrieve().bodyToMono(Details.class);
}
}
import org.springframework.boot.webclient.autoconfigure.WebClientSsl
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono
@Service
class MyService(webClientBuilder: WebClient.Builder, ssl: WebClientSsl) {
private val webClient: WebClient
init {
webClient = webClientBuilder.baseUrl("https://example.org")
.apply(ssl.fromBundle("mybundle")).build()
}
fun someRestCall(name: String): Mono<Details> {
return webClient.get().uri("/{name}/details", name)
.retrieve().bodyToMono(Details::class.java)
}
}
RestClient
如果您在應用程式中沒有使用 Spring WebFlux 或 Project Reactor,我們建議您使用 RestClient 來呼叫遠端 REST 服務。
RestClient 介面提供了一種函式式風格的命令式 API。
Spring Boot 會為您建立並預配置一個原型 RestClient.Builder bean。強烈建議您將其注入到您的元件中,並使用它來建立 RestClient 例項。Spring Boot 會為該構建器配置 HttpMessageConverters 和適當的 ClientHttpRequestFactory。
以下程式碼顯示了一個典型示例
-
Java
-
Kotlin
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
@Service
public class MyService {
private final RestClient restClient;
public MyService(RestClient.Builder restClientBuilder) {
this.restClient = restClientBuilder.baseUrl("https://example.org").build();
}
public Details someRestCall(String name) {
return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
}
}
import org.springframework.boot.docs.io.restclient.restclient.ssl.Details
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient
@Service
class MyService(restClientBuilder: RestClient.Builder) {
private val restClient: RestClient
init {
restClient = restClientBuilder.baseUrl("https://example.org").build()
}
fun someRestCall(name: String): Details {
return restClient.get().uri("/{name}/details", name)
.retrieve().body(Details::class.java)!!
}
}
RestClient 自定義
根據您希望自定義應用的範圍,有三種主要的 RestClient 自定義方法。
為了使任何自定義的範圍儘可能窄,請注入自動配置的 RestClient.Builder,然後根據需要呼叫其方法。RestClient.Builder 例項是有狀態的:對構建器的任何更改都會反映在使用它隨後建立的所有客戶端中。如果您想使用相同的構建器建立多個客戶端,您也可以考慮使用 RestClient.Builder other = builder.clone(); 克隆構建器。
若要對所有 RestClient.Builder 例項進行應用程式範圍的累加自定義,您可以宣告 RestClientCustomizer bean 並在注入點區域性更改 RestClient.Builder。
最後,您可以回退到原始 API 並使用 RestClient.create()。在這種情況下,不會應用任何自動配置或 RestClientCustomizer。
| 您還可以更改 全域性 HTTP 客戶端配置。 |
RestClient SSL 支援
如果您需要在 RestClient 使用的 ClientHttpRequestFactory 上進行自定義 SSL 配置,您可以注入一個 RestClientSsl 例項,該例項可以透過構建器的 apply 方法使用。
RestClientSsl 介面提供了對您在 application.properties 或 application.yaml 檔案中定義的任何 SSL 捆綁包的訪問。
以下程式碼顯示了一個典型示例
-
Java
-
Kotlin
import org.springframework.boot.restclient.autoconfigure.RestClientSsl;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
@Service
public class MyService {
private final RestClient restClient;
public MyService(RestClient.Builder restClientBuilder, RestClientSsl ssl) {
this.restClient = restClientBuilder.baseUrl("https://example.org").apply(ssl.fromBundle("mybundle")).build();
}
public Details someRestCall(String name) {
return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
}
}
import org.springframework.boot.docs.io.restclient.restclient.ssl.settings.Details
import org.springframework.boot.restclient.autoconfigure.RestClientSsl
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient
@Service
class MyService(restClientBuilder: RestClient.Builder, ssl: RestClientSsl) {
private val restClient: RestClient
init {
restClient = restClientBuilder.baseUrl("https://example.org")
.apply(ssl.fromBundle("mybundle")).build()
}
fun someRestCall(name: String): Details {
return restClient.get().uri("/{name}/details", name)
.retrieve().body(Details::class.java)!!
}
}
除了 SSL 捆綁包之外,如果您還需要應用其他自定義,可以使用 ClientHttpRequestFactorySettings 類和 ClientHttpRequestFactoryBuilder
-
Java
-
Kotlin
import java.time.Duration;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.HttpClientSettings;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
@Service
public class MyService {
private final RestClient restClient;
public MyService(RestClient.Builder restClientBuilder, SslBundles sslBundles) {
HttpClientSettings settings = HttpClientSettings.ofSslBundle(sslBundles.getBundle("mybundle"))
.withReadTimeout(Duration.ofMinutes(2));
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactoryBuilder.detect().build(settings);
this.restClient = restClientBuilder.baseUrl("https://example.org").requestFactory(requestFactory).build();
}
public Details someRestCall(String name) {
return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
}
}
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.HttpClientSettings
import org.springframework.boot.ssl.SslBundles
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient
import java.time.Duration
@Service
class MyService(restClientBuilder: RestClient.Builder, sslBundles: SslBundles) {
private val restClient: RestClient
init {
val settings = HttpClientSettings.defaults()
.withReadTimeout(Duration.ofMinutes(2))
.withSslBundle(sslBundles.getBundle("mybundle"))
val requestFactory = ClientHttpRequestFactoryBuilder.detect().build(settings);
restClient = restClientBuilder
.baseUrl("https://example.org")
.requestFactory(requestFactory).build()
}
fun someRestCall(name: String): Details {
return restClient.get().uri("/{name}/details", name).retrieve().body(Details::class.java)!!
}
}
RestTemplate
Spring Framework 的 RestTemplate 類早於 RestClient,是許多應用程式呼叫遠端 REST 服務的經典方式。當您有不想遷移到 RestClient 的現有程式碼時,或者因為您已經熟悉 RestTemplate API 時,您可能會選擇使用 RestTemplate。
由於 RestTemplate 例項在使用前通常需要自定義,因此 Spring Boot 不提供任何單一的自動配置 RestTemplate bean。但是,它會自動配置一個 RestTemplateBuilder,可用於在需要時建立 RestTemplate 例項。自動配置的 RestTemplateBuilder 確保將合理的 HttpMessageConverters 和適當的 ClientHttpRequestFactory 應用於 RestTemplate 例項。
以下程式碼顯示了一個典型示例
-
Java
-
Kotlin
import org.springframework.boot.restclient.RestTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class MyService {
private final RestTemplate restTemplate;
public MyService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
public Details someRestCall(String name) {
return this.restTemplate.getForObject("/{name}/details", Details.class, name);
}
}
import org.springframework.boot.restclient.RestTemplateBuilder
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate
@Service
class MyService(restTemplateBuilder: RestTemplateBuilder) {
private val restTemplate: RestTemplate
init {
restTemplate = restTemplateBuilder.build()
}
fun someRestCall(name: String): Details {
return restTemplate.getForObject("/{name}/details", Details::class.java, name)!!
}
}
RestTemplateBuilder 包含許多有用的方法,可用於快速配置 RestTemplate。例如,要新增 BASIC 身份驗證支援,您可以使用 builder.basicAuthentication("user", "password").build()。
RestTemplate 自定義
根據您希望自定義應用的範圍,有三種主要的 RestTemplate 自定義方法。
為了使任何自定義的範圍儘可能窄,請注入自動配置的 RestTemplateBuilder,然後根據需要呼叫其方法。每個方法呼叫都會返回一個新的 RestTemplateBuilder 例項,因此自定義只會影響此次構建器的使用。
要進行應用程式範圍的累加自定義,請使用 RestTemplateCustomizer bean。所有此類 bean 都會自動註冊到自動配置的 RestTemplateBuilder,並應用於使用它構建的任何模板。
以下示例展示了一個自定義器,它為除 192.168.0.5 之外的所有主機配置代理的使用。
-
Java
-
Kotlin
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.springframework.boot.restclient.RestTemplateCustomizer;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
public class MyRestTemplateCustomizer implements RestTemplateCustomizer {
@Override
public void customize(RestTemplate restTemplate) {
HttpRoutePlanner routePlanner = new CustomRoutePlanner(new HttpHost("proxy.example.com"));
HttpClient httpClient = HttpClientBuilder.create().setRoutePlanner(routePlanner).build();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
}
static class CustomRoutePlanner extends DefaultProxyRoutePlanner {
CustomRoutePlanner(HttpHost proxy) {
super(proxy);
}
@Override
protected HttpHost determineProxy(HttpHost target, HttpContext context) throws HttpException {
if (target.getHostName().equals("192.168.0.5")) {
return null;
}
return super.determineProxy(target, context);
}
}
}
import org.apache.hc.client5.http.classic.HttpClient
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner
import org.apache.hc.client5.http.routing.HttpRoutePlanner
import org.apache.hc.core5.http.HttpException
import org.apache.hc.core5.http.HttpHost
import org.apache.hc.core5.http.protocol.HttpContext
import org.springframework.boot.restclient.RestTemplateCustomizer
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory
import org.springframework.web.client.RestTemplate
class MyRestTemplateCustomizer : RestTemplateCustomizer {
override fun customize(restTemplate: RestTemplate) {
val routePlanner: HttpRoutePlanner = CustomRoutePlanner(HttpHost("proxy.example.com"))
val httpClient: HttpClient = HttpClientBuilder.create().setRoutePlanner(routePlanner).build()
restTemplate.requestFactory = HttpComponentsClientHttpRequestFactory(httpClient)
}
internal class CustomRoutePlanner(proxy: HttpHost?) : DefaultProxyRoutePlanner(proxy) {
@Throws(HttpException::class)
public override fun determineProxy(target: HttpHost, context: HttpContext): HttpHost? {
if (target.hostName == "192.168.0.5") {
return null
}
return super.determineProxy(target, context)
}
}
}
最後,您可以定義自己的 RestTemplateBuilder bean。這樣做會替換自動配置的構建器。如果您希望將任何 RestTemplateCustomizer bean 應用於您的自定義構建器(就像自動配置會做的那樣),請使用 RestTemplateBuilderConfigurer 對其進行配置。以下示例展示了一個 RestTemplateBuilder,它與 Spring Boot 自動配置所做的一致,只是還指定了自定義的連線和讀取超時。
-
Java
-
Kotlin
import java.time.Duration;
import org.springframework.boot.restclient.RestTemplateBuilder;
import org.springframework.boot.restclient.autoconfigure.RestTemplateBuilderConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyRestTemplateBuilderConfiguration {
@Bean
public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer configurer) {
return configurer.configure(new RestTemplateBuilder())
.connectTimeout(Duration.ofSeconds(5))
.readTimeout(Duration.ofSeconds(2));
}
}
import org.springframework.boot.restclient.autoconfigure.RestTemplateBuilderConfigurer
import org.springframework.boot.restclient.RestTemplateBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.time.Duration
@Configuration(proxyBeanMethods = false)
class MyRestTemplateBuilderConfiguration {
@Bean
fun restTemplateBuilder(configurer: RestTemplateBuilderConfigurer): RestTemplateBuilder {
return configurer.configure(RestTemplateBuilder()).connectTimeout(Duration.ofSeconds(5))
.readTimeout(Duration.ofSeconds(2))
}
}
最極端(且很少使用)的選項是建立自己的 RestTemplateBuilder bean,而不使用配置器。除了替換自動配置的構建器之外,這還會阻止使用任何 RestTemplateCustomizer bean。
| 您還可以更改 全域性 HTTP 客戶端配置。 |
RestTemplate SSL 支援
如果您需要在 RestTemplate 上進行自定義 SSL 配置,您可以將 SSL 捆綁包 應用於 RestTemplateBuilder,如本例所示
-
Java
-
Kotlin
import org.springframework.boot.docs.io.restclient.resttemplate.Details;
import org.springframework.boot.restclient.RestTemplateBuilder;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class MyService {
private final RestTemplate restTemplate;
public MyService(RestTemplateBuilder restTemplateBuilder, SslBundles sslBundles) {
this.restTemplate = restTemplateBuilder.sslBundle(sslBundles.getBundle("mybundle")).build();
}
public Details someRestCall(String name) {
return this.restTemplate.getForObject("/{name}/details", Details.class, name);
}
}
import org.springframework.boot.docs.io.restclient.resttemplate.Details
import org.springframework.boot.ssl.SslBundles
import org.springframework.boot.restclient.RestTemplateBuilder
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate
@Service
class MyService(restTemplateBuilder: RestTemplateBuilder, sslBundles: SslBundles) {
private val restTemplate: RestTemplate
init {
restTemplate = restTemplateBuilder.sslBundle(sslBundles.getBundle("mybundle")).build()
}
fun someRestCall(name: String): Details {
return restTemplate.getForObject("/{name}/details", Details::class.java, name)!!
}
}
RestClient 和 RestTemplate 的 HTTP 客戶端檢測
Spring Boot 將根據應用程式類路徑中可用的庫自動檢測用於 RestClient 和 RestTemplate 的 HTTP 客戶端。按優先順序順序,支援以下客戶端
-
Apache HttpClient
-
Jetty HttpClient
-
Reactor Netty HttpClient
-
JDK 客戶端(
java.net.http.HttpClient) -
Simple JDK 客戶端(
java.net.HttpURLConnection)
如果類路徑上有多個客戶端可用,並且沒有提供全域性配置,將使用優先順序最高的客戶端。
全域性 HTTP 客戶端配置
如果自動檢測到的 HTTP 客戶端不滿足您的需求,您可以使用 spring.http.clients.imperative.factory 屬性來選擇特定的工廠。例如,如果您的類路徑中有 Apache HttpClient,但您更喜歡 Jetty 的 HttpClient,您可以新增以下內容
-
屬性
-
YAML
spring.http.clients.imperative.factory=jetty
spring:
http:
clients:
imperative:
factory: jetty
| 您還可以使用適用於所有 HTTP 客戶端的全域性配置屬性。 |
對於更復雜的自定義,您可以使用 ClientHttpRequestFactoryBuilderCustomizer 或宣告您自己的 ClientHttpRequestFactoryBuilder bean,這將導致自動配置回退。當您需要自定義底層 HTTP 庫的一些內部實現時,這會很有用。
例如,以下程式碼將使用配置了特定 ProxySelector 的 JDK 客戶端
-
Java
-
Kotlin
import java.net.ProxySelector;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyClientHttpConfiguration {
@Bean
ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder(ProxySelector proxySelector) {
return ClientHttpRequestFactoryBuilder.jdk()
.withHttpClientCustomizer((builder) -> builder.proxy(proxySelector));
}
}
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.net.ProxySelector
@Configuration(proxyBeanMethods = false)
class MyClientHttpConfiguration {
@Bean
fun clientHttpRequestFactoryBuilder(proxySelector: ProxySelector): ClientHttpRequestFactoryBuilder<*> {
return ClientHttpRequestFactoryBuilder.jdk()
.withHttpClientCustomizer { builder -> builder.proxy(proxySelector) }
}
}
API 版本控制
WebClient 和 RestClient 都支援進行帶版本控制的遠端 HTTP 呼叫,以便 API 可以隨著時間演進。通常,這涉及傳送一個 HTTP 頭、一個查詢引數或一個 URL 路徑段,以指示應使用的 API 版本。
您可以使用 WebClient.Builder 或 RestClient.Builder 上的方法配置 API 版本控制。
| 伺服器端也支援 API 版本控制。詳細資訊請參見 Spring MVC 和 Spring WebFlux 部分。 |
| 伺服器端 API 版本控制配置不計入客戶端的自動配置。客戶端(通常用於測試)應使用 API 版本控制策略,需要明確配置。 |
HTTP 服務介面客戶端
除了直接使用 RestClient 或 WebClient 呼叫 HTTP 服務外,還可以使用帶註解的 Java 介面呼叫它們。
HTTP 服務介面透過使用 @HttpExchange 註解(或更常見的特定方法變體,如 @GetExchange、@PostExchange、@DeleteExchange 等)註解的方法來定義服務契約。
例如,以下程式碼定義了一個“echo”API 的 HTTP 服務,該 API 將返回一個包含請求回顯的 JSON 物件。
-
Java
-
Kotlin
import java.util.Map;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.service.annotation.HttpExchange;
import org.springframework.web.service.annotation.PostExchange;
@HttpExchange(url = "https://echo.zuplo.io")
public interface EchoService {
@PostExchange
Map<?, ?> echo(@RequestBody Map<String, String> message);
}
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.service.annotation.HttpExchange
import org.springframework.web.service.annotation.PostExchange
@HttpExchange(url = "https://echo.zuplo.io")
interface EchoService {
@PostExchange
fun echo(@RequestBody message: Map<String, String>): Map<*, *>
}
有關如何開發 HTTP 服務介面客戶端的更多詳細資訊,請參閱 Spring Framework 參考文件。
匯入 HTTP 服務
為了將 HTTP 服務介面用作客戶端,您需要匯入它。實現此目的的一種方法是使用 @ImportHttpServices 註解,通常在您的主應用程式類上。您可以使用該註解匯入特定類,或掃描特定包中的類以進行匯入。
例如,以下配置將掃描 com.example.myclients 包中的 HTTP 服務介面
-
Java
-
Kotlin
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.service.registry.ImportHttpServices;
@SpringBootApplication
@ImportHttpServices(basePackages = "com.example.myclients")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.web.service.registry.ImportHttpServices
@SpringBootApplication
@ImportHttpServices(basePackages = ["com.example.myclients"])
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
服務客戶端組
在生產應用程式中,在 @HttpExchange 註解中硬編碼絕對 URL 通常不是理想的選擇。相反,您通常希望在程式碼中為 HTTP 服務客戶端提供一個邏輯名稱,然後根據該名稱從屬性中查詢 URL。
HTTP 服務客戶端允許您透過將其註冊到命名組中來實現此目的。HTTP 服務組是共享共同特性的 HTTP 服務介面的集合。
例如,我們可能希望定義一個“echo”組,用於呼叫 https://echo.zuplo.io 的 HTTP 服務客戶端。
| HTTP 服務組可以用來定義除了 URL 之外的更多內容。例如,您的組可以定義連線超時和 SSL 設定。您還可以將客戶端自定義邏輯與組關聯,例如新增程式碼以插入所需的授權頭。 |
在使用 @ImportHttpServices 時,要將 HTTP 服務介面與組關聯,您可以使用 group 屬性。
例如,如果我們的上述示例以 com.example.myclients 包中的所有 HTTP 服務介面都屬於 echo 組的方式組織。我們首先從服務介面中刪除硬編碼的 URL
-
Java
-
Kotlin
import java.util.Map;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.service.annotation.PostExchange;
public interface EchoService {
@PostExchange
Map<?, ?> echo(@RequestBody Map<String, String> message);
}
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.service.annotation.HttpExchange
import org.springframework.web.service.annotation.PostExchange
@HttpExchange("echo")
interface EchoService {
@PostExchange
fun echo(@RequestBody message: Map<String, String>): Map<*, *>
}
然後我們可以寫
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.service.registry.ImportHttpServices;
@SpringBootApplication
@ImportHttpServices(group = "echo", basePackages = "com.example.myclients")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
最後,我們可以使用 base-url 屬性將 echo 組連結到實際的 URL
-
屬性
-
YAML
spring.http.serviceclient.echo.base-url=https://echo.zuplo.io
spring:
http:
serviceclient:
echo:
base-url: "https://echo.zuplo.io"
| 如果您未指定組,HTTP 服務客戶端將與名為“default”的組關聯。 |
|
如果同一個包中有多個 HTTP 服務介面需要與不同的組關聯,您可以單獨列出它們。 例如:
|
配置屬性
HTTP 服務的配置屬性可以在 spring.http.serviceclient.<group-name> 下指定。
您可以使用屬性配置以下方面:
-
基本 URL。
-
任何應傳送的預設頭。
-
API 版本控制配置。
-
重定向設定。
-
連線和讀取超時。
-
要使用的 SSL 捆綁包。
| 您還可以使用適用於所有 HTTP 客戶端的全域性配置屬性。 |
例如,以下屬性將
-
配置所有 HTTP 客戶端使用一秒連線超時(除非另行覆蓋)。
-
配置“echo”組中的 HTTP 服務客戶端以
-
使用特定的基本 URL。
-
擁有兩秒的讀取超時。
-
使用
X-Version頭插入 API 版本資訊。
-
-
屬性
-
YAML
spring.http.clients.connect-timeout=1s
spring.http.serviceclient.echo.base-url=https://echo.zuplo.io
spring.http.serviceclient.echo.read-timeout=2s;
spring.http.serviceclient.echo.apiversion.default=1.0.0
spring.http.serviceclient.echo.apiversion.insert.header=X-Version
spring:
http:
clients:
connect-timeout: 1s
serviceclient:
echo:
base-url: "https://echo.zuplo.io"
read-timeout: 2s;
apiversion:
default: 1.0.0
insert:
header: X-Version
自定義
如果您需要對 HTTP 服務客戶端進行超出基本屬性的自定義,您可以使用 HTTP 服務組配置器。對於 RestClient 支援的 HTTP 服務客戶端,您可以宣告一個實現 RestClientHttpServiceGroupConfigurer 的 bean。對於 WebClient 支援的 HTTP 服務客戶端,您可以宣告一個實現 WebClientHttpServiceGroupConfigurer 的 bean。
兩者以相同的方式工作,並將由 Spring Boot 的自動配置自動應用。
例如,以下配置將新增一個組自定義器,該自定義器向每個傳出請求新增一個包含組名稱的 HTTP 頭。
-
Java
-
Kotlin
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer;
@Configuration(proxyBeanMethods = false)
public class MyHttpServiceGroupConfiguration {
@Bean
RestClientHttpServiceGroupConfigurer myHttpServiceGroupConfigurer() {
return (groups) -> groups.forEachClient((group, clientBuilder) -> {
String groupName = group.name();
clientBuilder.defaultHeader("service-group", groupName);
});
}
}
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer
@Configuration(proxyBeanMethods = false)
class MyHttpServiceGroupConfiguration {
@Bean
fun myHttpServiceGroupConfigurer(): RestClientHttpServiceGroupConfigurer {
return RestClientHttpServiceGroupConfigurer { groups ->
groups.forEachClient { group, clientBuilder ->
val groupName = group.name()
clientBuilder.defaultHeader("service-group", groupName)
}
}
}
}
高階配置
除了 @ImportHttpServices 註解外,Spring Framework 還提供了一個 AbstractHttpServiceRegistrar 類。您可以 @Import 您自己的此類擴充套件,以執行程式設計配置。有關更多詳細資訊,請參閱 Spring Framework 參考文件。
無論您使用哪種方法註冊 HTTP 服務客戶端,Spring Boot 的支援都保持不變。
將全域性配置應用於所有 HTTP 客戶端
無論底層技術如何,所有 HTTP 客戶端都有可以配置的共同設定。
這些包括
-
連線超時。
-
讀取超時。
-
如何處理 HTTP 重定向。
-
連線時應使用哪個 SSL 捆綁包。
這些通用設定由 HttpClientSettings 類表示,可以傳遞給 ClientHttpConnectorBuilder 和 ClientHttpRequestFactoryBuilder 的 build(…) 方法。
如果您希望將相同的配置應用於所有自動配置的客戶端,可以使用 spring.http.clients 屬性來實現
-
屬性
-
YAML
spring.http.clients.connect-timeout=2s
spring.http.clients.read-timeout=1s
spring.http.clients.redirects=dont-follow
spring:
http:
clients:
connect-timeout: 2s
read-timeout: 1s
redirects: dont-follow