调用 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 Reactive HTTP 客户端库 org.eclipse.jetty:jetty-reactive-httpclient 的依赖。为服务器和客户端使用相同的技术具有其优势,因为它将自动在客户端和服务器之间共享 HTTP 资源。

开发人员可以通过提供自定义的 ReactorResourceFactoryJettyResourceFactory 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.propertiesapplication.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.propertiesapplication.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 将根据应用程序类路径中可用的库自动检测用于 RestClientRestTemplate 的 HTTP 客户端。按优先级顺序,支持以下客户端

  1. Apache HttpClient

  2. Jetty HttpClient

  3. Reactor Netty HttpClient

  4. JDK 客户端(java.net.http.HttpClient

  5. 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 版本控制

WebClientRestClient 都支持进行带版本控制的远程 HTTP 调用,以便 API 可以随着时间演进。通常,这涉及发送一个 HTTP 头、一个查询参数或一个 URL 路径段,以指示应使用的 API 版本。

您可以使用 WebClient.BuilderRestClient.Builder 上的方法配置 API 版本控制。

服务器端也支持 API 版本控制。详细信息请参见 Spring MVCSpring WebFlux 部分。
服务器端 API 版本控制配置不计入客户端的自动配置。客户端(通常用于测试)应使用 API 版本控制策略,需要明确配置。

HTTP 服务接口客户端

除了直接使用 RestClientWebClient 调用 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 服务接口需要与不同的组关联,您可以单独列出它们。@ImportHttpServices 是可重复的,types 属性允许您导入单个类。

例如:

  • Java

  • Kotlin

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.service.registry.ImportHttpServices;

@SpringBootApplication
@ImportHttpServices(group = "echo", types = EchoService.class)
@ImportHttpServices(group = "other", types = OtherService.class)
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(group = "echo", types = [EchoService::class])
@ImportHttpServices(group = "other", types = [OtherService::class])
class MyApplication

fun main(args: Array<String>) {
	runApplication<MyApplication>(*args)
}

配置属性

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 类表示,可以传递给 ClientHttpConnectorBuilderClientHttpRequestFactoryBuilderbuild(…​) 方法。

如果您希望将相同的配置应用于所有自动配置的客户端,可以使用 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
© . This site is unofficial and not affiliated with VMware.