呼叫 REST 服務
Spring Boot 提供了多種方便的方式來呼叫遠端 REST 服務。如果你正在開發一個非阻塞響應式應用程式並使用 Spring WebFlux,那麼可以使用 WebClient
。如果你更喜歡阻塞 API,那麼可以使用 RestClient
或 RestTemplate
。
WebClient
如果你的 classpath 中包含 Spring WebFlux,我們建議你使用 WebClient
來呼叫遠端 REST 服務。WebClient
介面提供函式式風格的 API,並且是完全響應式的。你可以在 Spring Framework 文件的專用章節中瞭解更多關於 WebClient
的資訊。
如果你不是在編寫響應式 Spring WebFlux 應用程式,則可以使用 RestClient 代替 WebClient 。它提供了類似的函式式 API,但是是阻塞的而不是響應式的。 |
Spring Boot 為你建立並預配置了一個原型 WebClient.Builder
bean。強烈建議將其注入到你的元件中,並使用它來建立 WebClient
例項。Spring Boot 配置該 builder 以共享 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 Runtime
Spring Boot 會根據應用程式 classpath 中可用的庫自動檢測使用哪個 ClientHttpConnector
來驅動 WebClient
。按照偏好順序,支援以下客戶端
-
Reactor Netty
-
Jetty RS 客戶端
-
Apache HttpClient
-
JDK HttpClient
如果 classpath 中有多個客戶端可用,將使用優先順序最高的客戶端。
預設情況下,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
配置選項的資訊。
WebClient 自定義
WebClient
自定義主要有三種方法,具體取決於你希望自定義應用於多大範圍。
為了使任何自定義的應用範圍儘可能小,請注入自動配置的 WebClient.Builder
,然後根據需要呼叫其方法。WebClient.Builder
例項是有狀態的:builder 上的任何更改都會反映在隨後使用它建立的所有客戶端中。如果你想使用相同的 builder 建立多個客戶端,也可以考慮使用 WebClient.Builder other = builder.clone();
克隆 builder。
要對所有 WebClient.Builder
例項進行應用範圍的附加自定義,可以宣告 WebClientCustomizer
bean,並在注入點本地更改 WebClient.Builder
。
最後,你可以退回到原始 API 並使用 WebClient.create()
。在這種情況下,不會應用自動配置或 WebClientCustomizer
。
WebClient SSL 支援
如果你需要在 ClientHttpConnector
使用的 WebClient
上進行自定義 SSL 配置,可以注入一個 WebClientSsl
例項,該例項可與 builder 的 apply
方法一起使用。
的 WebClientSsl
介面提供了訪問你在 application.properties
或 application.yaml
檔案中定義的任何 SSL 捆綁包的能力。
以下程式碼顯示了一個典型示例
-
Java
-
Kotlin
import reactor.core.publisher.Mono;
import org.springframework.boot.autoconfigure.web.reactive.function.client.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.autoconfigure.web.reactive.function.client.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 配置該 builder 使用 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
例項是有狀態的:builder 上的任何更改都會反映在隨後使用它建立的所有客戶端中。如果你想使用相同的 builder 建立多個客戶端,也可以考慮使用 RestClient.Builder other = builder.clone();
克隆 builder。
要對所有 RestClient.Builder
例項進行應用範圍的附加自定義,可以宣告 RestClientCustomizer
bean,並在注入點本地更改 RestClient.Builder
。
最後,你可以退回到原始 API 並使用 RestClient.create()
。在這種情況下,不會應用自動配置或 RestClientCustomizer
。
你還可以更改全域性 HTTP 客戶端配置。 |
RestClient SSL 支援
如果你需要在 ClientHttpRequestFactory
使用的 RestClient
上進行自定義 SSL 配置,可以注入一個 RestClientSsl
例項,該例項可與 builder 的 apply
方法一起使用。
RestClientSsl
介面提供了訪問你在 application.properties
或 application.yaml
檔案中定義的任何 SSL 捆綁包的能力。
以下程式碼顯示了一個典型示例
-
Java
-
Kotlin
import org.springframework.boot.autoconfigure.web.client.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.autoconfigure.web.client.RestClientSsl
import org.springframework.boot.docs.io.restclient.restclient.ssl.settings.Details
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.ClientHttpRequestFactorySettings;
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) {
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings
.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.ClientHttpRequestFactorySettings;
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 = ClientHttpRequestFactorySettings.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.web.client.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.web.client.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
例項,因此自定義僅影響本次 builder 的使用。
要進行應用範圍的附加自定義,請使用 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.web.client.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.web.client.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。這樣做將替換自動配置的 builder。如果你希望將任何 RestTemplateCustomizer
bean 應用於你的自定義 builder(就像自動配置所做的那樣),請使用 RestTemplateBuilderConfigurer
對其進行配置。以下示例展示了一個 RestTemplateBuilder
,它與 Spring Boot 自動配置所做的匹配,只是還指定了自定義的連線和讀取超時時間
-
Java
-
Kotlin
import java.time.Duration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateBuilderConfigurer;
import org.springframework.boot.web.client.RestTemplateBuilder;
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.autoconfigure.web.client.RestTemplateBuilderConfigurer
import org.springframework.boot.web.client.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))
}
}
最極端的(並且很少使用)選項是不使用 configurer 建立自己的 RestTemplateBuilder
bean。除了替換自動配置的 builder 之外,這還會阻止使用任何 RestTemplateCustomizer
bean。
你還可以更改全域性 HTTP 客戶端配置。 |
RestTemplate SSL 支援
如果你需要在 RestTemplate
上進行自定義 SSL 配置,可以將 SSL 捆綁包應用於 RestTemplateBuilder
,如本例所示
-
Java
-
Kotlin
import org.springframework.boot.docs.io.restclient.resttemplate.Details;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.client.RestTemplateBuilder;
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.web.client.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 會根據應用程式 classpath 中可用的庫自動檢測與 RestClient
和 RestTemplate
一起使用的 HTTP 客戶端。按照偏好順序,支援以下客戶端
-
Apache HttpClient
-
Jetty HttpClient
-
Reactor Netty HttpClient
-
JDK 客戶端 (
java.net.http.HttpClient
) -
簡單 JDK 客戶端 (
java.net.HttpURLConnection
)
如果 classpath 中有多個客戶端可用,並且沒有提供全域性配置,將使用優先順序最高的客戶端。
全域性 HTTP 客戶端配置
如果自動檢測到的 HTTP 客戶端不符合你的需求,可以使用 spring.http.client.factory
屬性選擇特定的工廠。例如,如果你的 classpath 中有 Apache HttpClient,但你更喜歡 Jetty 的 HttpClient
,可以新增以下內容
-
Properties
-
YAML
spring.http.client.factory=jetty
spring:
http:
client:
factory: jetty
你還可以設定屬性來更改將應用於所有客戶端的預設值。例如,你可能想更改超時時間和是否跟隨重定向
-
Properties
-
YAML
spring.http.client.connect-timeout=2s
spring.http.client.read-timeout=1s
spring.http.client.redirects=dont-follow
spring:
http:
client:
connect-timeout: 2s
read-timeout: 1s
redirects: dont-follow
對於更復雜的自定義,你可以宣告自己的 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
import java.net.http.HttpClient
@Configuration(proxyBeanMethods = false)
class MyClientHttpConfiguration {
@Bean
fun clientHttpRequestFactoryBuilder(proxySelector: ProxySelector): ClientHttpRequestFactoryBuilder<*> {
return ClientHttpRequestFactoryBuilder.jdk()
.withHttpClientCustomizer { builder -> builder.proxy(proxySelector) }
}
}