RestTemplate 中 exchange()、postForEntity() 和 execute() 之间的区别

1、简介

在本教程中,我们将了解 RestTemplate 类中 exchange()、postForEntity() 和 execute() 方法之间的区别。

2、RestTemplate 是啥?

RestTemplate 是 Spring 框架 中的一个工具类,它能让发送 HTTP 消息和处理响应变得简单。RestTemplate 类提供了许多功能,非常适合编写简单的 HTTP 客户端:

  • 支持所有标准 HTTP 方法(GET、POST 等)。
  • 能够处理所有标准 MIME Type(JSON、XML、表单等)。
  • 高级 API 允许我们使用 Java 代码进行配置,并避免复杂的序列化问题。
  • 可使用 ClientHttpRequestInitializer 和 ClientHttpRequestInterceptor 接口进行自定义。

2.1、废弃警告

从 Spring 5 开始,RestTemplate 类逐渐被弃用。虽然它在 Spring 6 中仍然存在,但维护者已经明确表示,该类今后不会再有任何改进,只接受较小的错误和安全修复。

因此,我们鼓励开发人员使用 WebClient。该类拥有更现代的 API,支持同步、异步和 stream 场景。

3、RestTemplate 基本用法

RestTemplate 通过提供具有相应名称的 public 方法,让使用标准 HTTP 方法变得简单。

例如,要发送 GET 请求,我们可以使用带有 getFor 前缀的多种重载方法之一。其他 HTTP 方法也类似,包括 POST、PUT、DELETE、HEAD 和 PATCH。

所有这些方法的结构几乎相同。它们基本上只需要有关的 URL 信息,以及请求和响应体的表示方法。header 等信息会自动生成。

虽然这些高级方法让编写 HTTP 客户端变得非常容易,但现实世界并不总是像 HTTP 规范那样。在某些情况下,我们可能需要构建一个不完全符合特定 HTTP 方法的 HTTP 请求。

这就是为什么 RestTemplate 提供了很多粗粒度的通用方法的原因,接下来我们将了解这些方法。

4、使用 exchange()、postForEntity() 和 execute() 方法

使用预定义的各种 HTTP 请求方法很方便,但有时我们可能需要对 RestTemplate 生成的 HTTP 请求进行更多控制。这时,exchange() 和 execute() 方法就派上用场了。

让我们举一个 HTTP POST 请求的例子,下面是一个 Java 类,它封装了请求体所需的所有数据:

class Book {
  String title;
  String author;
  int yearPublished;
}

下面我们将分别使用三种 RestTemplate 方法来发送此请求。

4.1、使用 postForEntity() 方法

发送 POST 请求的第一种也是最简单的方法是使用 postForEntity()。该方法只需要 URL 和请求体,并将响应体解析为一个 ResponseEntity 对象:

Book book = new Book(
  "Cruising Along with Java",
  "Venkat Subramaniam",
  2023);

 ResponseEntity<Book> response = restTemplate.postForEntity(
  "https://api.bookstore.com", 
  book, 
  Book.class);

在这个示例中,我们创建了一个新的 book 对象,将其发送到服务器,然后将响应解析为另一个 book 对象。值得注意的是,我们只需提供远程 URL、请求对象和响应使用的类。其他一切,包括 HTTP Header 信息,都是由 RestTemplate 自动构建的。

4.2、使用 exchange() 方法

接下来,使用 exchange() 方法发送请求:

Book book = new Book(
  "Effective Java",
  "Joshua Bloch",
  2001);

HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth("username", "password");

ResponseEntity<Book> response = restTemplate.exchange(
  "https://api.bookstore.com",
  HttpMethod.POST,
  new HttpEntity<>(book, headers),
  Book.class);

这里的主要区别在于,我们不是传递一个普通的 Java 对象作为请求体,而是用 HttpEntity 对其进行封装。这样,我们就可以在请求中明确设置额外的 HTTP 请求头信息。

另一个明显的区别是,exchange() 方法是通用的,这意味着它可以用于任何 HTTP 方法。因此,URL 后的第二个参数必须指明请求使用的方法。

4.3、使用 execute() 方法

最后一种方式是使用 execute() 方法。这种方法是最通用的,事实上,它是 RestTemplate 中所有其他方法使用的底层方法。

下面是如何使用 execute() 方法发送 POST 请求的示例:

ResponseEntity<Book> response = restTemplate.execute(
    "https://api.bookstore.com",
    HttpMethod.POST,
    new RequestCallback() {
        @Override
        public void doWithRequest(ClientHttpRequest request) throws IOException {
            // 操作请求头和请求体
        }
    },
    new ResponseExtractor<ResponseEntity<Book>>() {
        @Override
        public ResponseEntity<Book> extractData(ClientHttpResponse response) throws IOException {
            // 操作响应并返回 ResponseEntity
        }
    }
);

值得注意的是,虽然我们仍然需要提供 URL 和 HTTP 方法,但其他一切看起来都大不相同。这是因为 execute() 方法并不直接处理请求和响应。

相反,它让我们能够分别使用 RequestCallback 和 ResponseExtractor 接口来创建和修改它们。这样做的主要好处是,我们可以最大程度地控制请求和响应对象。弊端就是,我们的代码不够简洁,而且失去了许多其他 RestTemplate 方法提供的自动功能。

RestTemplate 提供了一个工厂方法,可以轻松创建 RequestCallback 实例。该方法名为 httpEntityCallback(),有两种重载形式,有助于减少我们在使用 execute() 方法时编写的代码量:

Book book = new Book(
  "Reactive Spring",
  "Josh Long",
  2020);
        
RequestCallback requestCallback1 = restTemplate.httpEntityCallback(book);
RequestCallback requestCallback2 = restTemplate.httpEntityCallback(book, Book.class);

同样,RestTemplate 也提供了一个工厂方法来快速创建 ResponseExtractor 的实例:

ResponseExtractor<ResponseEntity<Book>> responseExtractor = restTemplate.responseEntityExtractor(Book.class);

5、总结

在本文中,我们了解了使用 RestTemplate 发送 HTTP POST 请求的三种不同方法。首先,我们了解了如何使用特定于方法的 postForEntity() 方法来创建小而简洁的 HTTP 请求。然后,我们学习了两种替代方法 exchange() 和 execute() 来发送相同的请求。

虽然这三种方法都能产生相同的结果,但它们各有利弊。postForEntity() 方法产生的代码较少,但我们对结果 HTTP 请求的控制却较弱。exchange() 和 execute() 方法为我们提供了对请求的更多控制,但代价是我们的代码更加冗长,对 Spring 框架自动功能的依赖性也更低。


参考:https://www.baeldung.com/spring-resttemplate-exchange-postforentity-execute