调用 REST 服务

Spring Boot 提供了多种便捷的方式来调用远程 REST 服务。 如果你正在开发一个非阻塞的响应式应用,并且使用 Spring WebFlux,那么你可以使用Web客户端. 如果你更喜欢命令式 API,可以用Rest客户端Rest模板.spring-doc.cadn.net.cn

Web客户端

如果你的 classpath 里有 Spring WebFlux,我们建议你使用Web客户端调用远程REST服务。 这Web客户端接口提供功能式 API,并且完全响应式。 你可以了解更多关于Web客户端Spring Framework 文档的专门部分。spring-doc.cadn.net.cn

如果你不是在编写响应式 Spring WebFlux 应用,可以使用Rest客户端而不是Web客户端. 这提供了类似的功能API,但是命令式的,而非响应式的。

Spring Boot 创建并预配置原型WebClient.Builder给你来个豆子。 强烈建议将其注入组件中并用来制造Web客户端实例。 Spring Boot 正在配置该构建器以共享 HTTP 资源,并以与服务器编解码器相同的方式反映编解码器(参见 WebFlux HTTP 编解码器自动配置)等。spring-doc.cadn.net.cn

以下代码展示了一个典型例子:spring-doc.cadn.net.cn

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用来开车Web客户端这取决于应用类路径上可用的库。 按优先顺序,支持以下客户端:spring-doc.cadn.net.cn

  1. 反应堆Nettyspring-doc.cadn.net.cn

  2. Jetty RS 客户端spring-doc.cadn.net.cn

  3. Apache HttpClientspring-doc.cadn.net.cn

  4. JDK HttpClientspring-doc.cadn.net.cn

如果类路径上有多个客户端,则会使用最优的客户端。spring-doc.cadn.net.cn

Spring BootStarters网流启动剂取决于io.projectreactor.netty:reactor-netty默认情况下,这带来了服务器和客户端的实现。 如果你选择使用 Jetty 作为响应式服务器,应添加对 Jetty 响应式 HTTP 客户端库的依赖,org.eclipse.jetty:jetty-reactive-httpclient. 服务器和客户端使用相同技术有其优势,因为它会自动在客户端和服务器之间共享HTTP资源。spring-doc.cadn.net.cn

开发者可以通过提供自定义配置来覆盖Jetty和Reactor Netty的资源配置反应堆资源工厂Jetty资源工厂BEAN - 这将同时应用于客户端和服务器。spring-doc.cadn.net.cn

如果你想为客户推翻这个选择,你可以定义自己的ClientHttpConnectorBean 并能完全控制客户端配置。spring-doc.cadn.net.cn

全局HTTP连接器配置

如果自动检测ClientHttpConnector如果不满足你的需求,你可以使用spring.http.clients.reactive.connector选择特定连接器的属性。 比如,如果你的职业路径上有反应堆Netty,但你更喜欢Jetty的Http客户端你可以补充以下内容:spring-doc.cadn.net.cn

spring.http.clients.reactive.connector=jetty
spring:
  http:
    clients:
      reactive:
        connector: jetty
你也可以使用适用于所有HTTP客户端的全局配置属性

如果需要更复杂的自定义,你可以用ClientHttpConnectorBuilderCustomizer或者自己申报ClientHttpConnectorBuilder这会导致自动配置功能退后。 当你需要自定义底层HTTP库的一些内部结构时,这非常有用。spring-doc.cadn.net.cn

例如,以下内容将使用配置为特定代理选择器:spring-doc.cadn.net.cn

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

}

Web客户端定制

主要有三种方法Web客户端定制,取决于你希望这些定制应用的范围。spring-doc.cadn.net.cn

为了尽可能缩小自定义范围,可以注入自动配置WebClient.Builder然后根据需要调用其方法。WebClient.Builder实例是有状态的:建造者的任何变更都会反映在随后创建的所有客户端中。 如果你想用同一个架构师创建多个客户端,也可以考虑克隆该架构师WebClient.Builder other = builder.clone();.spring-doc.cadn.net.cn

为所有应用实现可加的自定义WebClient.Builder实例,你可以声明WebClientCustomizer豆子和变革WebClient.Builder在注射点局部。spring-doc.cadn.net.cn

最后,你可以退回到原始 API,使用WebClient.create(). 在这种情况下,没有自动配置或WebClientCustomizer应用。spring-doc.cadn.net.cn

Web客户端SSL支持

如果你需要自定义的 SSL 配置,ClientHttpConnector被以下机构使用Web客户端,你可以注射一个WebClientSsl可以与架构商一起使用的实例应用方法。spring-doc.cadn.net.cn

WebClientSsl接口允许访问你在application.propertiesapplication.yaml文件。spring-doc.cadn.net.cn

以下代码展示了一个典型例子:spring-doc.cadn.net.cn

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

}

Rest客户端

如果你的应用程序中没有使用 Spring WebFlux 或 Project Reactor,我们建议你使用Rest客户端调用远程REST服务。spring-doc.cadn.net.cn

Rest客户端界面提供了函数式样式命令式API。spring-doc.cadn.net.cn

Spring Boot 创建并预配置原型RestClient.Builder给你来个豆子。 强烈建议将其注入组件中并用来制造Rest客户端实例。 Spring Boot 正在配置该建构器HttpMessage转换器以及适当的ClientHttpRequestFactory.spring-doc.cadn.net.cn

以下代码展示了一个典型例子:spring-doc.cadn.net.cn

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

}

Rest客户端定制

主要有三种方法Rest客户端定制,取决于你希望这些定制应用的范围。spring-doc.cadn.net.cn

为了尽可能缩小自定义范围,可以注入自动配置RestClient.Builder然后根据需要调用其方法。RestClient.Builder实例是有状态的:建造者的任何变更都会反映在随后创建的所有客户端中。 如果你想用同一个架构师创建多个客户端,也可以考虑克隆该架构师RestClient.Builder other = builder.clone();.spring-doc.cadn.net.cn

为所有应用实现可加的自定义RestClient.Builder实例,你可以声明RestClientCustomizer豆子和变革RestClient.Builder在注射点局部。spring-doc.cadn.net.cn

最后,你可以退回到原始 API,使用RestClient.create(). 在这种情况下,没有自动配置或RestClientCustomizer应用。spring-doc.cadn.net.cn

你也可以更改全局 HTTP 客户端配置

RestClient SSL 支持

如果你需要自定义的 SSL 配置,ClientHttpRequestFactory被以下机构使用Rest客户端,你可以注射一个RestClientSsl可以与架构商一起使用的实例应用方法。spring-doc.cadn.net.cn

RestClientSsl接口允许访问你在application.propertiesapplication.yaml文件。spring-doc.cadn.net.cn

以下代码展示了一个典型例子:spring-doc.cadn.net.cn

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捆绑包外还需要其他自定义,可以使用客户端HttpRequestFactorySettings类为ClientHttpRequestFactoryBuilder:spring-doc.cadn.net.cn

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

}

Rest模板

Spring Framework 的Rest模板该级别早于该级别Rest客户端是许多应用程序调用远程REST服务的经典方式。 你可以选择使用Rest模板当你有不想迁移的现有代码时Rest客户端或者因为你已经熟悉Rest模板应用程序接口。spring-doc.cadn.net.cn

因为Rest模板实例通常需要在使用前进行自定义,Spring Boot 不提供任何单一的自动配置功能Rest模板豆。 不过,它确实会自动配置Rest模板构建器,可以用来创建Rest模板需要时才会做。 自动配置Rest模板构建器确保理智HttpMessage转换器以及适当的ClientHttpRequestFactory被应用于Rest模板实例。spring-doc.cadn.net.cn

以下代码展示了一个典型例子:spring-doc.cadn.net.cn

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

}

Rest模板构建器包含多种可用于快速配置Rest模板. 例如,要添加BASIC认证支持,你可以使用builder.basicAuthentication(“用户”,“密码”).build().spring-doc.cadn.net.cn

RestTemplate 自定义

主要有三种方法Rest模板定制,取决于你希望这些定制应用的范围。spring-doc.cadn.net.cn

为了尽可能缩小自定义范围,可以注入自动配置Rest模板构建器然后根据需要调用其方法。 每个方法调用返回新的Rest模板构建器所以自定义只会影响构建器的使用。spring-doc.cadn.net.cn

要进行全应用的加法定制,请使用RestTemplateCustomizer豆。 所有此类豆子都会自动注册到自动配置的Rest模板构建器并应用于任何基于该模板构建的模板。spring-doc.cadn.net.cn

以下示例展示了一个自定义器,该编辑器配置了除192.168.0.5:spring-doc.cadn.net.cn

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

	}

}

最后,你可以定义自己的Rest模板构建器豆。 这样做将取代自动配置的架构。 如果你想要的话RestTemplateCustomizerBeans 要应用到你的自定义构建器上,就像自动配置那样,用RestTemplateBuilderConfigurer. 以下示例揭示了一个Rest模板构建器这与 Spring Boot 的自动配置相符,但还指定了自定义连接和读取超时:spring-doc.cadn.net.cn

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

}

最极端(且很少使用)的选项是自己创建Rest模板构建器没有使用配置器就能使用豆子。 除了替换自动配置的建造者外,这还防止了任何RestTemplateCustomizer豆子被使用。spring-doc.cadn.net.cn

你也可以更改全局 HTTP 客户端配置

RestTemplate SSL 支持

如果你需要自定义的 SSL 配置,Rest模板,你可以对Rest模板构建器如本例所示:spring-doc.cadn.net.cn

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 会自动检测到使用哪个 HTTP 客户端Rest客户端Rest模板这取决于应用类路径上可用的库。 按优先顺序,支持以下客户端:spring-doc.cadn.net.cn

  1. Apache HttpClientspring-doc.cadn.net.cn

  2. Jetty HttpClientspring-doc.cadn.net.cn

  3. Reactor Netty HttpClientspring-doc.cadn.net.cn

  4. JDK 客户端 (java.net.http.HttpClient)spring-doc.cadn.net.cn

  5. 简单 JDK 客户端 (java.net.HttpURLConnection)spring-doc.cadn.net.cn

如果类路径上有多个客户端,且未提供全局配置,则使用最优客户端。spring-doc.cadn.net.cn

全局 HTTP 客户端配置

如果自动检测的HTTP客户端不满足你的需求,你可以使用spring.http.clients.imperative.factory选择特定工厂的财产。 例如,如果你的类路径上有Apache HttpClient,但你更喜欢Jetty的Http客户端你可以补充以下内容:spring-doc.cadn.net.cn

spring.http.clients.imperative.factory=jetty
spring:
  http:
    clients:
      imperative:
        factory: jetty
你也可以使用适用于所有HTTP客户端的全局配置属性

如果需要更复杂的自定义,你可以用ClientHttpRequestFactoryBuilderCustomizer或者自己申报ClientHttpRequestFactoryBuilder这会导致自动配置功能退后。 当你需要自定义底层HTTP库的一些内部结构时,这非常有用。spring-doc.cadn.net.cn

例如,以下内容将使用配置为特定代理选择器:spring-doc.cadn.net.cn

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 版本管理

Web客户端Rest客户端支持进行版本化的远程HTTP调用,以便API能够随着时间演进。 通常这包括发送HTTP头部、查询参数或URL路径段,指示应使用的API版本。spring-doc.cadn.net.cn

你可以用 的方法配置 API 版本管理WebClient.BuilderRestClient.Builder.spring-doc.cadn.net.cn

服务器端也支持 API 版本控制。 详情请参见春季MVC春季WebFlux板块。
服务器端API版本控制配置未被考虑用于自动配置客户端。 那些应该使用 API 版本管理策略(通常用于测试)的客户端,需要明确配置它。

HTTP 服务接口客户端

而不是直接使用Rest客户端Web客户端调用HTTP服务时,也可以使用带注释的Java接口调用它们。spring-doc.cadn.net.cn

HTTP 服务接口通过使用注释为@HttpExchange,或者更常见的是方法特定的变体(@GetExchange,@PostExchange,@DeleteExchange,等等)。spring-doc.cadn.net.cn

例如,以下代码定义了一个“回声”API的HTTP服务,该API将返回包含请求回声的JSON对象。spring-doc.cadn.net.cn

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 参考文档中找到。spring-doc.cadn.net.cn

导入HTTP服务

要将HTTP服务接口作为客户端使用,你需要导入它。 实现这一点的一种方法是使用@ImportHttpServices注释,通常出现在你的主应用类上。 你可以用注释导入特定类,或者扫描从特定包导入的类。spring-doc.cadn.net.cn

例如,以下配置将扫描 HTTP 服务接口com.example.myclients包:spring-doc.cadn.net.cn

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

服务客户端组

硬编码绝对 URL@HttpExchange注释在生产应用中通常并不理想。 相反,你通常会想在代码中给HTTP Service客户端一个逻辑名称,然后基于该名称从属性中查找URL。spring-doc.cadn.net.cn

HTTP Service客户端通过将它们注册到命名组中来实现这一点。 HTTP 服务组是一组共享共同特征的 HTTP 服务接口。spring-doc.cadn.net.cn

例如,我们可能想定义一个“回声”组,用于调用HTTP服务的客户端https://echo.zuplo.io.spring-doc.cadn.net.cn

HTTP 服务组可以用来定义不仅仅是 URL。 例如,你们的团队可以定义连接超时和SSL设置。 你还可以将客户端自定义逻辑关联到一组,比如添加代码插入所需的授权头。

在使用@ImportHttpServices你可以使用属性。spring-doc.cadn.net.cn

例如,假设上述示例组织为所有HTTP服务接口com.example.myclients包裹属于回波群。 我们首先从服务界面中移除硬编码的URL:spring-doc.cadn.net.cn

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<*, *>

}

然后我们可以写成:spring-doc.cadn.net.cn

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用来链接回波组到实际的网址:spring-doc.cadn.net.cn

spring.http.serviceclient.echo.base-url=https://echo.zuplo.io
spring:
  http:
    serviceclient:
      echo:
        base-url: "https://echo.zuplo.io"
如果你没有指定某个组,HTTP Service 客户端会被关联到一个名为“default”的组。

如果你在同一包中有多个需要关联不同组的 HTTP 服务接口,你可以单独列出它们。 这@ImportHttpServices是可重复的,且类型属性功能允许你导入单个职业。spring-doc.cadn.net.cn

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>:spring-doc.cadn.net.cn

你可以使用属性来配置诸如:spring-doc.cadn.net.cn

你也可以使用适用于所有HTTP客户端的全局配置属性

例如,下面的属性将:spring-doc.cadn.net.cn

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 服务组配置器。 为Rest客户端支持的HTTP Service客户端,你可以声明一个实现RestClientHttpServiceGroupConfigurer. 为Web客户端支持的HTTP Service客户端你可以声明一个 bean来实现WebClientHttpServiceGroupConfigurer.spring-doc.cadn.net.cn

两者的工作原理相同,并会由 Spring Boot 的自动配置自动应用。spring-doc.cadn.net.cn

例如,以下配置会添加一个组自定义器,为每个包含组名的出站请求添加一个HTTP头:spring-doc.cadn.net.cn

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 参考文档spring-doc.cadn.net.cn

无论你用哪种方法注册 HTTP 服务客户端,Spring Boot 的支持都保持不变。spring-doc.cadn.net.cn

向所有HTTP客户端应用全局配置

无论底层技术为何,所有HTTP客户端都有可配置的通用设置。spring-doc.cadn.net.cn

这些包括:spring-doc.cadn.net.cn

这些常见的设置用HttpClientSettings可以传递给构建(...)方法ClientHttpConnectorBuilderClientHttpRequestFactoryBuilder.spring-doc.cadn.net.cn

如果你想对所有自动配置的客户端应用相同的配置,你可以使用spring.http.clients实现这一点的性质:spring-doc.cadn.net.cn

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