调用 REST 服务

Spring Boot 提供多种便捷的方式来调用远程 REST 服务。如果你正在开发一个非阻塞式响应式应用程序并使用 Spring WebFlux,那么你可以使用WebClient。如果你更喜欢阻塞式 API,那么你可以使用RestClientRestTemplate

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 将根据应用程序类路径上可用的库自动检测要用于驱动WebClientClientHttpConnector。按照优先级顺序,支持以下客户端:

  1. Reactor Netty

  2. Jetty RS 客户端

  3. Apache HttpClient

  4. JDK HttpClient

如果类路径中存在多个客户端,则将使用最优先的客户端。

spring-boot-starter-webflux 启动器默认依赖于 io.projectreactor.netty:reactor-netty,它同时提供了服务器和客户端实现。如果您选择使用 Jetty 作为响应式服务器,则应添加对 Jetty 响应式 HTTP 客户端库 org.eclipse.jetty:jetty-reactive-httpclient 的依赖。由于服务器和客户端使用相同的技术,因此它具有自身的优势,因为它会自动在客户端和服务器之间共享 HTTP 资源。

开发人员可以通过提供自定义的 ReactorResourceFactoryJettyResourceFactory bean 来覆盖 Jetty 和 Reactor Netty 的资源配置 - 这将应用于客户端和服务器。

如果您希望覆盖客户端的选择,您可以定义自己的 ClientHttpConnector bean 并完全控制客户端配置。

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.propertiesapplication.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 使用 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

RestClient SSL 支持

如果您需要对 RestClient 使用的 ClientHttpRequestFactory 进行自定义 SSL 配置,您可以注入一个 RestClientSsl 实例,该实例可与构建器的 apply 方法一起使用。

RestClientSsl 接口可访问您在 application.propertiesapplication.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 类与 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 实例,因此自定义仅会影响此构建器的使用。

为了进行应用程序范围的增量自定义,请使用 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。这样做将替换自动配置的构建器。如果您希望将任何 RestTemplateCustomizer bean 应用于您的自定义构建器(就像自动配置一样),请使用 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))
	}

}

最极端(而且很少使用)的选择是创建您自己的 RestTemplateBuilder bean,而不使用配置器。除了替换自动配置的构建器之外,这还会阻止使用任何 RestTemplateCustomizer bean。

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.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)!!
    }

}

RestClient 和 RestTemplate 的 HTTP 客户端检测

Spring Boot 将根据应用程序类路径上可用的库自动检测要与 RestClientRestTemplate 一起使用的 HTTP 客户端。按优先级顺序,支持以下客户端:

  1. Apache HttpClient

  2. Jetty HttpClient

  3. OkHttp(已弃用)

  4. 简单的 JDK 客户端(HttpURLConnection

如果类路径中存在多个客户端,则将使用最优先的客户端。