# 11.6. 调用 REST 服务

Spring Boot 提供了各种便捷的方式来调用远程 REST 服务。如果您正在开发非阻塞反应式应用程序并且正在使用 Spring WebFlux，那么您可以使用`WebClient`. 如果您更喜欢阻止 API，那么您可以使用`RestClient`或`RestTemplate`。

**11.6.1. WebClient**

如果您的类路径上有 Spring WebFlux，我们建议您使用`WebClient`来调用远程 REST 服务。`WebClient`界面提供了函数式 API，并且是完全响应式的。[您可以在 Spring 框架文档的](https://docs.spring.io/spring-framework/reference/6.1/web/webflux-webclient.html)`WebClient`专门部分了解更多相关信息。

> 如果您不编写响应式 Spring WebFlux 应用程序，则可以使用[`RestClient`](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#io.rest-client.restclient)替代`WebClient`. 这提供了类似的功能 API，但是阻塞式的而不是响应式的。

Spring Boot为您创建并预配置原型`WebClient.Builder`bean。强烈建议将其注入您的组件中并使用它来创建`WebClient`实例。Spring Boot 正在配置该构建器以共享 HTTP 资源并以与服务器相同的方式反映编解码器设置（请参阅[WebFlux HTTP 编解码器自动配置](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#web.reactive.webflux.httpcodecs)）等等。

下面的代码展示了一个典型的例子：

```
@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);
    }
​
}
```

**WebClient运行时**

Spring Boot 将根据应用程序类路径上可用的库自动检测使用哪个 `ClientHttpConnector` 来驱动`WebClient`。按照优先顺序，支持以下客户端：

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 资源。

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

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

[您可以在 Spring 框架`WebClient`参考文档中](https://docs.spring.io/spring-framework/reference/6.1/web/webflux-webclient/client-builder.html)了解有关配置选项的更多信息。

**WebClient定制**

自定义有三种主要方法`WebClient`，具体取决于您希望自定义应用的范围。

为了使任何自定义的范围尽可能缩小，请注入自动配置的`WebClient.Builder`内容，然后根据需要调用其方法。 `WebClient.Builder`实例是有状态的：构建器上的任何更改都会反映在随后使用它创建的所有客户端中。如果您想使用同一个构建器创建多个客户端，您还可以考虑使用`WebClient.Builder other = builder.clone();`.

要对所有`WebClient.Builder`实例进行应用程序范围的附加自定义，您可以声明`WebClientCustomizer`bean 并在注入点位置更改`WebClient.Builder`。

最后，您可以回退到原始 API 并使用`WebClient.create()`. 在这种情况下，不会应用任何`WebClientCustomizer`的自动配置。

**WebClient SSL 支持**

如果您需要在`ClientHttpConnector`所使用的`WebClient`上进行自定义 SSL 配置，您可以注入一个`WebClientSsl`可与构建器`apply`方法一起使用的实例。

该界面提供对您在`application.properties`或`application.yaml`文件中定义的任何[SSL 捆绑包的](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.ssl.bundles)`WebClientSsl`访问。

下面的代码展示了一个典型的例子：

```
@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);
    }
​
}
```

**11.6.2. Rest 客户端**

如果您在应用程序中没有使用 Spring WebFlux 或 Project Reactor，我们建议您使用`RestClient`调用远程 REST 服务。

`RestClient`接口提供了函数式阻塞API。

Spring Boot为您创建并预配置原型`RestClient.Builder`bean。强烈建议将其注入您的组件中并使用它来创建`RestClient`实例。Spring Boot 正在使用`HttpMessageConverters` 和适当的 `ClientHttpRequestFactory` 配置该构建器。

下面的代码展示了一个典型的例子：

```
@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);
    }
​
}
```

**RestClient定制**

自定义有三种主要方法`RestClient`，具体取决于您希望自定义应用的范围。

为了使任何自定义的范围尽可能缩小，请注入自动配置的`RestClient.Builder`内容，然后根据需要调用其方法。

`RestClient.Builder`实例是有状态的：构建器上的任何更改都会反映在随后使用它创建的所有客户端中。如果您想使用同一个构建器创建多个客户端，您还可以考虑使用`RestClient.Builder other = builder.clone();`.

要对所有`RestClient.Builder`实例进行应用程序范围的附加自定义，您可以声明`RestClientCustomizer`bean 并在注入点位置更改`RestClient.Builder`。

最后，您可以回退到原始 API 并使用`RestClient.create()`. 在这种情况下，不会应用任何`RestClientCustomizer`的自动配置。

**RestClient SSL 支持**

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

该界面提供对您在`application.properties`或`application.yaml`文件中定义的任何[SSL 捆绑包的](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.ssl.bundles)`RestClientSsl`访问。

下面的代码展示了一个典型的例子：

```
@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);
    }
​
}
```

如果除了 SSL 捆绑包之外您还需要应用其他自定义，则可以将`ClientHttpRequestFactorySettings`类与`ClientHttpRequestFactories`一起使用：

```
@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);
    }
​
}
```

**11.6.3. RestTemplate**

Spring Framework 的[`RestTemplate`](https://docs.spring.io/spring-framework/docs/6.1.1/javadoc-api/org/springframework/web/client/RestTemplate.html)类早于`RestClient`并且是许多应用程序用来调用远程 REST 服务的经典方式。`RestTemplate`当您不想迁移到现有代码`RestClient`，或者因为您已经熟悉该API 时，您可能会选择使用`RestTemplate`。

由于`RestTemplate`实例在使用之前通常需要进行自定义，因此 Spring Boot 不提供任何单个自动配置的`RestTemplate`bean。但是，它会自动配置 `RestTemplateBuilder`，可用于在需要时创建`RestTemplate`实例。自动配置`RestTemplateBuilder`可确保将合理`HttpMessageConverters`和适当的`ClientHttpRequestFactory`应用到`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);
    }
​
}
```

`RestTemplateBuilder`包括许多有用的方法，可用于快速配置`RestTemplate`. 例如，要添加 BASIC 身份验证支持，您可以使用`builder.basicAuthentication("user", "password").build()`.

**RestTemplate定制**

自定义有三种主要方法`RestTemplate`，具体取决于您希望自定义应用的范围。

为了使任何自定义的范围尽可能缩小，请注入自动配置的内容`RestTemplateBuilder`，然后根据需要调用其方法。每个方法调用都会返回一个新`RestTemplateBuilder`实例，因此自定义仅影响构建器的这种使用。

要进行应用程序范围的附加定制，请使用`RestTemplateCustomizer`bean。所有此类 bean 都会自动注册到自动配置`RestTemplateBuilder`，并应用于使用它构建的任何模板。

以下示例显示了一个定制程序，该定制程序为除以下主机`192.168.0.5`之外的所有主机配置代理的使用：

```
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);
        }
​
    }
​
}
```

最后，您可以定义自己的`RestTemplateBuilder`bean。这样做将替换自动配置的构建器。如果您希望将任何`RestTemplateCustomizer`bean 应用于您的自定义构建器（就像自动配置一样），请使用`RestTemplateBuilderConfigurer`. 以下示例公开了与 Spring Boot 的自动配置将执行的操作相匹配的`RestTemplateBuilder` ，除此之外还指定了自定义连接和读取超时：

```
@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));
    }
​
}
```

最极端（且很少使用）的选项是在不使用配置器的情况下创建您自己的`RestTemplateBuilder`bean。除了替换自动配置的构建器之外，这还可以防止使用任何`RestTemplateCustomizer` bean。

**RestTemplate SSL 支持**

如果您需要在 上进行自定义 SSL 配置`RestTemplate`，您可以将[SSL 捆绑包](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.ssl.bundles)应用于 `RestTemplateBuilder`，如本示例所示：

```
@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);
    }
​
}
```

**11.6.4. RestClient 和 RestTemplate 的 HTTP 客户端检测**

Spring Boot 将根据应用程序类路径上可用的库自动检测与 `RestClient` 和`RestTemplate` 一起使用的 HTTP 客户端。按照优先顺序，支持以下客户端：

1. Apache HttpClient
2. Jetty HttpClient
3. OkHttp（已弃用）
4. 简单的 JDK 客户端 ( `HttpURLConnection`)

如果类路径上有多个客户端可用，则将使用最首选的客户端。
