# 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`)

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://doc.shiker.tech/spring-boot-can-kao-wen-dang/11.-io/11.6.-diao-yong-rest-fu-wu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
