呼叫 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 正在配置該 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 執行期
Spring Boot 將自動偵測要使用哪個 ClientHttpConnector
來驅動 WebClient
,具體取決於應用程式類別路徑上可用的程式庫。依照偏好順序,支援以下客戶端
-
Reactor Netty
-
Jetty RS client
-
Apache HttpClient
-
JDK HttpClient
如果類別路徑上有多個客戶端可用,則將使用最優先的客戶端。
spring-boot-starter-webflux
starter 預設依賴於 io.projectreactor.netty:reactor-netty
,它同時帶來了伺服器和客戶端實作。如果您選擇使用 Jetty 作為反應式伺服器,則應新增對 Jetty Reactive HTTP client 程式庫 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 支援
如果您需要在 WebClient
使用的 ClientHttpConnector
上進行自訂 SSL 組態,您可以注入一個 WebClientSsl
實例,該實例可以與 builder 的 apply
方法一起使用。
WebClientSsl
介面提供了對您在 application.properties
或 application.yaml
檔案中定義的任何 SSL bundles 的存取權。
以下程式碼顯示了一個典型的範例
-
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 正在使用 HttpMessageConverters
和適當的 ClientHttpRequestFactory
來配置該 builder。
以下程式碼顯示了一個典型的範例
-
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
。
RestClient SSL 支援
如果您需要在 RestClient
使用的 ClientHttpRequestFactory
上進行自訂 SSL 組態,您可以注入一個 RestClientSsl
實例,該實例可以與 builder 的 apply
方法一起使用。
RestClientSsl
介面提供了對您在 application.properties
或 application.yaml
檔案中定義的任何 SSL bundles 的存取權。
以下程式碼顯示了一個典型的範例
-
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 bundle 之外還需要應用其他客製化,則可以將 ClientHttpRequestFactorySettings
類別與 ClientHttpRequestFactories
一起使用
-
Java
-
Kotlin
import java.time.Duration;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.client.ClientHttpRequestFactories;
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
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.DEFAULTS
.withReadTimeout(Duration.ofMinutes(2))
.withSslBundle(sslBundles.getBundle("mybundle"));
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(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.ssl.SslBundles
import org.springframework.boot.web.client.ClientHttpRequestFactories
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings
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 = ClientHttpRequestFactories.get(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
,並應用於使用它建置的任何模板。
以下範例顯示了一個 customizer,它配置了除 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())
.setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(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()).setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(Duration.ofSeconds(2))
}
}
最極端(且很少使用)的選項是在不使用 configurer 的情況下建立您自己的 RestTemplateBuilder
bean。除了取代自動配置的 builder 之外,這也阻止了任何 RestTemplateCustomizer
bean 被使用。
RestTemplate SSL 支援
如果您需要在 RestTemplate
上進行自訂 SSL 組態,您可以將 SSL bundle 應用於 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.setSslBundle(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.setSslBundle(sslBundles.getBundle("mybundle")).build()
}
fun someRestCall(name: String): Details {
return restTemplate.getForObject("/{name}/details", Details::class.java, name)!!
}
}