20220507 1. Integration - REST Endpoints

前言

文档地址

Spring 框架为调用 REST 端点提供了两种选择:

  • RestTemplate : 带有同步模板方法 API 的原始 Spring REST 客户端
  • WebClient : 一种非阻塞、反应式的替代方案,既支持同步方案,也支持异步方案以及流方案

在 5.0 版本中,RestTemplate 处于维护模式,只有少量的更改请求和错误修复被接受。请考虑使用 WebClient ,它提供了更现代的 API ,并支持同步、异步和流场景

RestTemplate

RestTemplate 通过 HTTP 客户端库提供了更高级别的 API 。它使得在单行中调用 REST 端点变得容易。它公开下列重载方法组:

方法组 描述
getForObject 通过 GET 检索一个表示
getForEntity 使用 GET 检索 ResponseEntity (即 status、 headers 和 body)
headForHeaders 使用 HEAD 检索资源的所有标头
postForLocation 使用 POST 创建新资源,并从响应返回 Location
postForObject 使用 POST 创建新资源,并从响应返回表示
postForEntity 使用 POST 创建新资源,并从响应返回表示
put 使用 PUT 创建或更新资源
patchForObject 使用 PATCH 更新资源并返回响应的表示形式。注意,JDK HttpURLConnection 不支持 PATCH ,但是 Apache HttpComponents 和其他组件支持 PATCH
delete 使用 DELETE 删除指定 URI 处的资源
optionsForAllow 使用 ALLOW 为资源检索允许的 HTTP 方法
exchange 更加通用,提供额外的灵活性。它接受一个 RequestEntity (包括 HTTP 方法、 URL、 header 和 body 作为输入) ,并返回一个 ResponseEntity
这些方法允许使用 ParameterizedTypeReference 而不是 Class 来指定具有泛型的响应类型
execute 执行请求的最通用方式,通过回调接口完全控制请求准备和响应提取

初始化(Initialization)

缺省构造函数使用 java.net.HttpURLConnection 来执行请求。您可以使用 ClientHttpRequestFactory 实现切换到不同的 HTTP 库。它内置了以下功能:

  • Apache HttpComponents
  • Netty
  • OkHttp

例如,要切换到 Apache HttpComponents ,可以使用以下命令:

RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());

每个 ClientHttpRequestFactory 都公开特定于底层 HTTP 客户端库的配置选项ーー例如,凭证、连接池和其他细节。

注意,HTTP 请求的 java.net 实现在访问表示错误的响应 (比如 401) 的状态时可能会引发异常。如果这是一个问题,切换到另一个 HTTP 客户端库。

URIs

许多 RestTemplate 方法接受 URI 模板和 URI 模板变量,或者作为 String 变量参数,或者作为 Map<String,String>

下面的示例使用了一个 String 变量参数:

String result = restTemplate.getForObject(
        "https://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");

下面的示例使用 Map<String, String>

Map<String, String> vars = Collections.singletonMap("hotel", "42");

String result = restTemplate.getForObject(
        "https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);

记住 URI 模板是自动编码的,如下面的例子所示:

restTemplate.getForObject("https://example.com/hotel list", String.class);

// Results in request to "https://example.com/hotel%20list"

您可以使用 RestTemplateuriTemplateHandler 属性来自定义 URI 的编码方式。或者,您可以准备一个 java.net.URI ,并将其传递到接受 URIRestTemplate 方法之一。

有关使用和编码 URI 的详细信息,请参阅 URI Links

Headers

您可以使用 exchange() 方法来指定请求头,如下面的示例所示:

String uriTemplate = "https://example.com/hotels/{hotel}";
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).build(42);

RequestEntity<Void> requestEntity = RequestEntity.get(uri)
        .header("MyRequestHeader", "MyValue")
        .build();

ResponseEntity<String> response = template.exchange(requestEntity, String.class);

String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();

您可以通过许多返回 ResponseEntityRestTemplate 方法变体获得响应标头。

Body

HttpMessageConverter 的帮助下,从 RestTemplate 方法传入和返回的对象将转换为原始内容。

在 POST 上,输入对象被序列化到请求主体,如下面的示例所示:

URI location = template.postForLocation("https://example.com/people", person);

您不需要显式地设置请求的 Content-Type 标头。在大多数情况下,您可以找到基于源 Object 类型的兼容消息转换器,并且所选择的消息转换器相应地设置内容类型。如果有必要,可以使用 exchange 方法显式地提供 Content-Type 请求标头,而这又会影响选择哪个消息转换器。

在 GET 上,响应的主体被反序列化为输出对象,如下面的示例所示:

Person person = restTemplate.getForObject("https://example.com/people/{id}", Person.class, 42);

请求的 Accept 标头不需要显式设置。在大多数情况下,可以根据预期的响应类型找到兼容的消息转换器,这有助于填充 Accept 头。如果需要,可以使用 exchange 方法显式地提供 Accept 标头。

默认情况下,RestTemplate 注册所有内置的 消息转换器 ,这取决于类路径检查,该检查有助于确定存在哪些可选转换库。您还可以将消息转换器设置为显式使用。

消息转换

WebFlux

spring-web 模块包含 HttpMessageConverter 契约,用于通过 InputStreamOutputStream 读写 HTTP 请求和响应的主体。HttpMessageConverter 实例用于客户端 (例如,在 RestTemplate 中) 和服务器端 (例如,在 Spring MVC REST 控制器中) 。

框架中提供了主要媒体 (MIME) 类型的具体实现,并且默认情况下,在客户端使用 RestTemplate 注册,在服务器端使用 RequestMethodHandlerAdapter 注册 (参见 配置消息转换器 )

以下几节将描述 HttpMessageConverter 的实现。对于所有转换器,都使用默认的媒体类型,但是可以通过设置 supportedMediaTypes bean 属性来覆盖它。下表介绍了 HttpMessageConverter 实现:

MessageConverter 描述
StringHttpMessageConverter 一个 HttpMessageConverter 实现,可以从 HTTP 请求和响应中读写 String 实例。默认情况下,此转换器支持所有文本媒体类型 ( text/* ) ,并使用 text/plainContent-Type )进行写入
FormHttpMessageConverter 一个 HttpMessageConverter 实现,可以从 HTTP 请求和响应中读取和写入表单数据。默认情况下,此转换器读取和写入 application/x-www-form-urlencoded 媒体类型。表单数据被读取并写入 MultiValueMap<String, String> 。该转换器还可以写入(但不读取) 从 MultiValueMap<String, Object> 读取的多部分数据。默认情况下,支持 multipart/form-data 。从 Spring Framework 5.2 开始,可以支持其他多部分子类型来写入表单数据。有关详细信息,请参阅 FormHttpMessageConverter
ByteArrayHttpMessageConverter 一个 HttpMessageConverter 实现,可以从 HTTP 请求和响应中读取和写入字节数组。默认情况下,此转换器支持所有媒体类型 ( */* ) ,并使用 Content-Type application/octet-stream 进行写操作。可以通过设置 supportedMediaTypes 属性和重写 getContentType(byte[]) 来重写此属性
MarshallingHttpMessageConverter 一个 HttpMessageConverter 实现,可以使用 Spring 的 MarshallerUnmarshallerorg.springframework.oxm 包中抽象出来读写 XML 。这个转换器在使用之前需要一个 MarshallerUnmarshaller 。可以通过构造函数或 bean 属性注入这些属性。默认情况下,此转换器支持 text/xmlapplication/xml
MappingJackson2HttpMessageConverter 一个 HttpMessageConverter 实现,可以使用 Jackson 的 ObjectMapper 读写 JSON。您可以根据需要使用 Jackson 提供的注解来定制 JSON 映射。当您需要进一步的控制时 (对于需要为特定类型提供自定义 JSON 序列化程序/反序列化器的情况) ,可以通过 ObjectMapper 属性注入自定义 ObjectMapper 。默认情况下,此转换器支持 application/json
MappingJackson2XmlHttpMessageConverter 一个 HttpMessageConverter 实现,可以使用 Jackson XML 扩展的 XmlMapper 读写 XML 。您可以根据需要使用 JAXB 或 Jackson 提供的注解来定制 XML 映射。当您需要进一步的控制时 (对于需要为特定类型提供自定义 XML 序列化程序/反序列化器的情况) ,可以通过 ObjectMapper 属性注入自定义 XmlMapper 。默认情况下,此转换器支持 application/xml
SourceHttpMessageConverter 可以读写 javax.xml.transform.SourceHttpMessageConverter 实现。源自 HTTP 请求和响应。只支持 DOMSourceSAXSourceStreamSource 。默认情况下,此转换器支持 text/xmlapplication/xml
BufferedImageHttpMessageConverter 可读写来自 HTTP 请求和响应里的 java.awt.image.BufferedImageHttpMessageConverter 实现。这个转换器读写 Java I/O API 支持的媒体类型

Jackson JSON Views

可以指定一个 Jackson JSON 视图来序列化对象属性的一个子集

MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23"));
value.setSerializationView(User.WithoutPasswordView.class);

RequestEntity<MappingJacksonValue> requestEntity =
    RequestEntity.post(new URI("https://example.com/user")).body(value);

ResponseEntity<String> response = template.exchange(requestEntity, String.class);
Multipart

要发送多部分数据,您需要提供 MultiValueMap<String, Object> ,其值可以是部分内容的 Object ,文件部分的 Resource ,或者带有标头的部分内容的 HttpEntity 。例如:

MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();

parts.add("fieldPart", "fieldValue");
parts.add("filePart", new FileSystemResource("...logo.png"));
parts.add("jsonPart", new Person("Jason"));

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
parts.add("xmlPart", new HttpEntity<>(myBean, headers));

在大多数情况下,您不必为每个部件指定 Content-Type 。内容类型是基于序列化它所选择的 HttpMessageConverter 自动确定的,或者在基于文件扩展名的资源的情况下。如果有必要,可以显式地为 MediaType 提供一个 HttpEntity 包装器。

一旦 MultiValueMap 准备好了,你可以将它传递给 RestTemplate ,如下所示:

MultiValueMap<String, Object> parts = ...;
template.postForObject("https://example.com/upload", parts, Void.class);

如果 MultiValueMap 包含至少一个非 String 值,则 FormHttpMessageConverterContent-Type 设置为 multipart/form-data 。如果 MultiValueMap 具有 String 值,则 Content-Type 默认为 application/x-www-form-urlencoded 。如果需要,也可以显式设置 Content-Type

使用 AsyncRestTemplate (已弃用)

不推荐使用 AsyncRestTemplate 。对于所有可能考虑使用 AsyncRestTemplate 的用例,请使用 WebClient

posted @ 2022-06-09 21:29  流星<。)#)))≦  阅读(32)  评论(0编辑  收藏  举报