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

1、简介

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

2、RestTemplate 是啥?

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

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

2.1、废弃警告

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

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

3、RestTemplate 基本用法

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

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

所有这些方法的结构几乎相同。它们基本上只需要有关的 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() 方法并不直接处理请求和响应。

相反,它让我们能够分别使用 RequestCallbackResponseExtractor 接口来创建和修改它们。这样做的主要好处是,我们可以最大程度地控制请求和响应对象。弊端就是,我们的代码不够简洁,而且失去了许多其他 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