Spring RestClient 教程
1、简介
RestClient
是 Spring 6.1 M2 中引入的同步 HTTP 客户端,它取代了 RestTemplate
。同步 HTTP 客户端以阻塞方式发送和接收 HTTP 请求和响应,这意味着它会等待每个请求完成后才继续下一个请求。
本文将带你了解 RestClient
的功能以及它与 RestTemplate
的比较。
2、RestClient
和 RestTemplate
RestTemplate
,顾名思义,是基于模板设计模式构建的。它是一种行为设计模式,用于在方法中定义算法的框架,允许子类为某些步骤提供特定的实现。虽然这是一种强大的模式,但它会导致需要进行方法覆写,这可能是不方便的。
为了改进这一点,RestClient
采用了 Fluent API。Fluent API 也是一种设计模式,它允许以一种使代码更易读和表达的方式进行方法链式调用,通常无需使用中间变量。
从创建一个基本的 RestClient
开始:
RestClient restClient = RestClient.create();
3、使用各种 HTTP 方法发起请求
与 RestTemplate
或其他 REST 客户端类似,RestClient
可以使用不同的 HTTP 方法发起请求。
创建一个用于操作的 Article
类:
public class Article {
Integer id;
String title;
// 构造函数和 getter/setter
}
3.1、使用 GET 请求获取资源
GET 请求用于检索 Web 服务器上指定资源的数据,而不对其进行修改。通常,这是一个只读的操作!
获取服务器响应的字符串(String
):
String result = restClient.get()
.uri(uriBase + "/articles")
.retrieve()
.body(String.class);
3.2、使用 POST 请求创建资源
POST 请求用于提交数据到 Web 服务器上,进行新的资源创建。资源通常是通过 URI 定义的。
向服务器发送一篇 ID 为 1
的 Article
:
Article article = new Article(1, "How to use RestClient");
ResponseEntity<Void> response = restClient.post()
.uri(uriBase + "/articles")
.contentType(APPLICATION_JSON)
.body(article)
.retrieve()
.toBodilessEntity();
由于指定了 APPLICATION_JSON
Content Type,Jackson 库会自动将 Article
类实例序列化为 JSON。在本例中,使用 toBodilessEntity()
方法忽略了响应体。POST 端点不需要返回任何 Payload,通常也不会返回任何 Payload。
3.3、使用 PUT 请求更新资源
接下来是 PUT 请求,用于更新或替换已经存在的资源。
修改上一段中创建的 Article
,URI 中的 ID 即表示要修改的资源:
Article article = new Article(1, "How to use RestClient even better");
ResponseEntity<Void> response = restClient.put()
.uri(uriBase + "/articles/1")
.contentType(APPLICATION_JSON)
.body(article)
.retrieve()
.toBodilessEntity();
与 POST 请求一样,依靠 RestClient
对 Payload 进行序列化,并忽略响应。
3.4、使用 DELETE 请求删除资源
使用 DELETE 请求从 Web 服务器上删除指定资源。与 GET 类似,通常不提供任何 Payload,而是在 URI 参数中指定要删除的资源:
ResponseEntity<Void> response = restClient.delete()
.uri(uriBase + "/articles/1")
.retrieve()
.toBodilessEntity();
4、反序列化响应
通常,我们希望将请求序列化,并将响应反序列化为我们可以操作的类。RestClient
具有执行 JSON 到对象转换的功能,该功能由 Jackson
库提供。
此外,由于共享使用了 Message Converter,因此可以使用 RestTemplate
支持的所有数据类型。
通过 ID 获取一篇文章,并将其序列化为 Article
类的实例:
Article article = restClient.get()
.uri(uriBase + "/articles/1")
.retrieve()
.body(Article.class);
当我们需要获取 List
等泛型类的实例时,指定 body 的类就比较复杂了。例如,如果我们想获取所有 Article
,以 List<Article>
对象形式返回。在这种情况下,可以使用 ParameterizedTypeReference
抽象类来告诉 RestClient
我们要获取什么对象。
甚至不需要指定泛型类型,Java 会自动推断出类型:
List<Article> articles = restClient.get()
.uri(uriBase + "/articles")
.retrieve()
.body(new ParameterizedTypeReference<>() {});
5、使用 Exchange 解析响应
RestClient
包含 exchange()
方法。通过提供对底层 HTTP 请求和响应的访问权限,用于处理更复杂的情况。在这种情况下,库不会应用默认的 Handler,我们必须自己处理响应的 Status。
比方说,当数据库中没有文章时,服务会返回 204
(No Content)状态代码。由于这种行为略显非标准,我们希望以一种特殊的方式来处理它。当状态代码等于 204
时,抛出一个 ArticleNotFoundException
异常;当状态代码不等于 200 时,抛出一个更通用的异常:
List<Article> article = restClient.get()
.uri(uriBase + "/articles")
.exchange((request, response) -> {
if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(204))) {
throw new ArticleNotFoundException();
} else if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(200))) {
return objectMapper.readValue(response.getBody(), new TypeReference<>() {});
} else {
throw new InvalidArticleResponseException();
}
});
由于我们在这里处理的是原始响应,因此还需要使用 ObjectMapper
对 Response Body 进行反序列化。
6、异常处理
默认情况下,当 RestClient
在 HTTP 响应中遇到 4xx
或 5xx
状态码时,它会抛出一个 RestClientException
子类的异常。我们可以通过自定义 Status Handler 来覆盖这一行为。
自定义 Status Handler,在找不到文章时抛出一个自定义异常:
Article article = restClient.get()
.uri(uriBase + "/articles/1234")
.retrieve()
.onStatus(status -> status.value() == 404, (request, response) -> {
throw new ArticleNotFoundException(response)
})
.body(Article.class);
7、从 RestTemplate 构建 RestClient
如果你已经在项目中使用上了 RestTemplate
,也可以可从 RestTemplate
构建 RestClient
。
RestTemplate oldRestTemplate;
RestClient restClient = RestClient.create(oldRestTemplate);
8、总结
本文介绍了 RestClient
的特点、用法、反序列化和异常处理。这是一个新的的 Rest 客户端,设计更加优雅,旨在代替掉 RestTemplate
。
参考:https://www.baeldung.com/spring-boot-restclient