首页 > 其他分享 >Spring 教程—REST 客户端详解

Spring 教程—REST 客户端详解

时间:2023-04-17 18:05:25浏览次数:33  
标签:HTTP String 一个 Spring RestTemplate REST 响应 请求 客户端


Spring框架为调用REST端点提供了以下选择:

  • WebClient - 非阻塞、响应式客户端和 fluent API。
  • RestTemplate - 带有模板方法API的同步客户端。
  • HTTP 接口 - 注解式接口,并生成动态代理实现。

一、 WebClient

WebClient 是一个非阻塞的、响应式的客户端,用于执行HTTP请求。它在5.0中引入,提供了 RestTemplate 的替代方案,支持同步、异步和流式场景。

WebClient 支持以下特性:

  • 非阻塞 I/O。
  • Reactive Streams 背压。
  • 以较少的硬件资源实现高并发性。
  • 函数式、fluent API,利用了Java 8 lambdas的优势。
  • 同步和异步互动。
  • 服务器的流式上传和下载。

详情见WebClient 原理及实践—官方原版

二、RestTemplate

RestTemplate 提供了一个比HTTP客户端库更高层次的API。它使得在一行中调用REST端点变得容易。它暴露了以下几组重载方法: 

Table 1. RestTemplate methods

方法组

说明

getForObject

通过 GET 检索一个表示结果。

getForEntity

通过使用GET检索一个 ResponseEntity(即 status、header 和 body)。

headForHeaders

通过使用 HEAD 检索一个资源的所有 header。

postForLocation

通过使用 POST 创建一个新的资源,并从响应中返回 Location header。

postForObject

通过使用POST创建一个新资源,并从响应中返回表示。

postForEntity

通过使用POST创建一个新资源,并从响应中返回表示。

put

通过使用PUT创建或更新一个资源。

patchForObject

通过使用 PATCH 更新一个资源,并从响应中返回表示。请注意,JDK的 HttpURLConnection 不支持 PATCH,但 Apache 的 HttpComponents 和其他的支持。

delete

通过使用 DELETE 删除指定URI上的资源。

optionsForAllow

通过使用 ALLOW 为资源检索允许的HTTP方法。

exchange

前面的方法的更通用(和更少的意见)版本,在需要时提供额外的灵活性。它接受一个 RequestEntity(包括HTTP方法、URL、header 和 body 作为输入)并返回一个 ResponseEntity

这些方法允许使用 ParameterizedTypeReference 而不是 Class 来指定一个具有泛型的响应类型。

execute

执行请求的最通用方式,通过回调接口对请求准备和响应提取进行完全控制。

1、初始化

默认构造函数使用 java.net.HttpURLConnection 来执行请求。你可以通过 ClientHttpRequestFactory 的实现切换到不同的HTTP库。有内置的对以下内容的支持:

  • Apache HttpComponents
  • Netty
  • OkHttp

例如,要切换 到Apache HttpComponents,你可以使用以下方法:

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

Spring 教程—REST 客户端详解_客户端

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

URI

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

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

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

Spring 教程—REST 客户端详解_客户端_02

下面的例子使用一个 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);

Spring 教程—REST 客户端详解_xml_03

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

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

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

Spring 教程—REST 客户端详解_xml_04

你可以使用 RestTemplate 的 uriTemplateHandler 属性来定制URI的编码方式。或者,你可以准备一个 java.net.URI,并把它传入接受 URI 的 RestTemplate 方法之一。

Header

你可以使用 exchange() 方法来指定 header,如下例所示:

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();

Spring 教程—REST 客户端详解_xml_05

你可以通过许多返回 ResponseEntity 的 RestTemplate 方法变体获得响应头信息。

2、Body

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

在POST中,一个输入对象被序列化到请求体中,如下面的例子所示:

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

Spring 教程—REST 客户端详解_HTTP_06

你不需要明确地设置请求的 Content-Type 头。在大多数情况下,你可以根据源对象类型找到一个兼容的消息转换器(message converter),所选择的消息转换器会相应地设置 content type。如果有必要,你可以使用 exchange 方法来明确地提供 Content-Type 的请求头,而这又会影响到选择何种消息转换器。

在一个GET中,响应的 body 被反序列化为一个输出 Object,如下例所示:

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

Spring 教程—REST 客户端详解_xml_07

请求的 Accept 标头不需要明确设置。在大多数情况下,可以根据预期的响应类型找到一个兼容的消息转换器,然后帮助填充 Accept 头。如果有必要,你可以使用 exchange 方法来明确提供 Accept 头。 默认情况下,RestTemplate 注册了所有内置的 消息转换器(message converter),这取决于 classpath 检查,有助于确定有哪些可选的 converter 库存在。你也可以明确地设置要使用的消息转换器。

消息转换

spring-web 模块包含 HttpMessageConverter contract,用于通过 InputStream 和 OutputStream 读写 HTTP 请求和响应的 body。HttpMessageConverter 实例在客户端(例如,在 RestTemplate 中)和服务器端(例如,在Spring MVC REST controller 中)使用。

框架中提供了主要 type(MIME)type 的具体实现,默认情况下,在客户端与 RestTemplate 注册,在服务器端与 RequestMappingHandlerAdapter 注册

HttpMessageConverter 的实现将在下面几节中描述。对于所有的转换器,都使用默认的 media type,但是你可以通过设置 supportedMediaTypes bean 属性来覆盖它。下表描述了每个实现:

Table 2. HttpMessageConverter 实现

MessageConverter

说明

StringHttpMessageConverter

一个 HttpMessageConverter 实现,可以从HTTP请求和响应中读写 String 实例。默认情况下,这个转换器支持所有的文本媒体类型(text/*),并以 text/plain 的 Content-Type 进行写入。

FormHttpMessageConverter

一个 HttpMessageConverter 实现,可以从HTTP请求和响应中读写表单数据。默认情况下,这个转换器读取和写入 application/x-www-form-urlencoded 媒体类型。表单数据从一个 MultiValueMap<String, String> 中读取并写入。该转换器还可以写入(但不读取)从 MultiValueMap<String, Object> 中读取的 multipart 数据。默认情况下,支持 multipart/form-data。从Spring Framework 5.2开始,可以支持额外的 multipart subtype 来写入表单数据。请查阅 FormHttpMessageConverter 的 javadoc 以了解更多细节。

ByteArrayHttpMessageConverter

一个 HttpMessageConverter 实现,可以从HTTP请求和响应中读写字节数。默认情况下,这个转换器支持所有的 media type(*/*),并以 application/octet-stream 为 Content-Type 进行写入。你可以通过设置 supportedMediaTypes 属性和覆盖 getContentType(byte[]) 来重写这一点。

MarshallingHttpMessageConverter

一个 HttpMessageConverter 实现,通过使用 org.springframework.oxm 包中 Spring 的 Marshaller 和 Unmarshaller 抽象,可以读写XML。这个转换器在使用前需要一个 Marshaller 和 Unmarshaller。你可以通过构造函数或Bean属性注入这些东西。默认情况下,这个转换器支持 text/xml 和 application/xml

MappingJackson2HttpMessageConverter

一个 HttpMessageConverter 实现,通过使用 Jackson 的 ObjectMapper 可以读写JSON。你可以通过使用Jackson提供的注解,根据需要定制JSON映射。当你需要进一步控制时(对于需要为特定类型提供自定义JSON serializers/deserializers 的情况),你可以通过 ObjectMapper 属性注入一个自定义 ObjectMapper。默认情况下,这个转换器支持 application/json

MappingJackson2XmlHttpMessageConverter

一个 HttpMessageConverter 的实现,可以通过使用 Jackson XML 扩展的 XmlMapper 来读写XML。你可以根据需要通过使用JAXB或Jackson提供的注解来定制XML映射。当你需要进一步控制时(对于需要为特定类型提供自定义XML serializers/deserializers 的情况),你可以通过 ObjectMapper 属性注入一个自定义 XmlMapper。默认情况下,这个转换器支持 application/xml

SourceHttpMessageConverter

一个 HttpMessageConverter 实现,可以从HTTP请求和响应中读写 javax.xml.transform.Source。只有 DOMSourceSAXSource 和 StreamSource 被支持。默认情况下,这个转换器支持 text/xml 和 application/xml

BufferedImageHttpMessageConverter

一个 HttpMessageConverter 实现,可以从HTTP请求和响应中读写 java.awt.image.BufferedImage。这个转换器读取和写入Java I/O API支持的 media type。

3、Jackson JSON 视图

你可以指定一个 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);

Spring 教程—REST 客户端详解_xml_08

4、Multipart

为了发送 multipart 数据,你需要提供一个 MultiValueMap<String, Object>,其值可以是一个用于 part 内容的 Object,一个用于文件 part 的 Resource,或者一个用于带有 header 的 part 内容的 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));

Spring 教程—REST 客户端详解_HTTP_09

在大多数情况下,你不需要为每个 part 指定 Content-Type。content type 是根据为序列化它而选择的 HttpMessageConverter 自动确定的,或者在 Resource 的情况下,根据文件扩展名确定。如果有必要,你可以用一个 HttpEntity wrapper 明确地提供 MediaType

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

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

Spring 教程—REST 客户端详解_客户端_10

如果 MultiValueMap 至少包含一个非 String 值,Content-Type 就被 FormHttpMessageConverter 设置为 multipart/form-data。如果 MultiValueMap 有 String 值,Content-Type 被默认为 application/x-www-form-urlencoded。如果有必要,也可以明确设置 Content-Type

三、HTTP 接口

Spring框架允许你将HTTP服务定义为一个Java接口,并为 HTTP exchange 提供注解方法。然后你可以生成一个实现该接口并执行 exchange 的代理。这有助于简化HTTP远程访问,因为远程访问通常涉及到一个 facade,该 facade 包装了使用底层HTTP客户端的细节。

声明一个带有 @HttpExchange 方法的接口:

interface RepositoryService {

    @GetExchange("/repos/{owner}/{repo}")
    Repository getRepository(@PathVariable String owner, @PathVariable String repo);

    // more HTTP exchange methods...

}

Spring 教程—REST 客户端详解_客户端_11

创建一个代理,执行所声明的 HTTP exchange:

WebClient client = WebClient.builder().baseUrl("https://api.github.com/").build();
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();

RepositoryService service = factory.createClient(RepositoryService.class);

Spring 教程—REST 客户端详解_xml_12

在类型层面上支持 @HttpExchange,它适用于所有方法:

@HttpExchange(url = "/repos/{owner}/{repo}", accept = "application/vnd.github.v3+json")
interface RepositoryService {

    @GetExchange
    Repository getRepository(@PathVariable String owner, @PathVariable String repo);

    @PatchExchange(contentType = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    void updateRepository(@PathVariable String owner, @PathVariable String repo,
            @RequestParam String name, @RequestParam String description, @RequestParam String homepage);

}

Spring 教程—REST 客户端详解_xml_13

1、方法参数

注解式 HTTP exchange 方法支持灵活的方法签名,有以下方法参数:

方法参数

说明

URI

动态设置请求的URL,覆盖注解的 url 属性。

HttpMethod

动态地设置请求的HTTP方法,覆盖注解的 method 属性

@RequestHeader

添加一个请求头或多个头。参数可以是一个 Map<String, ?> 或 MultiValueMap<String, ?> 的多个头,一个 Collection<?>,或一个单独的值。对于非 String 值,支持类型转换。

@PathVariable

添加一个变量,用于在请求URL中扩展一个占位符。参数可以是一个带有多个变量的 Map<String, ?>,也可以是一个单独的值。对于非 String 值,支持类型转换。

@RequestBody

提供请求的 body,可以是要被序列化的对象,也可以是Reactive Streams Publisher,如 MonoFlux,或通过配置的 ReactiveAdapterRegistry 支持的任何其他异步类型。

@RequestParam

添加一个请求参数或多个参数。参数可以是一个 Map<String, ?> 或 MultiValueMap<String, ?> 的多个参数,一个 Collection<?> ,或一个单独的值。对于非 String 值,支持类型转换。

当 "content-type" 被设置为 "application/x-www-form-urlencoded" 时,请求参数被编码在请求体中。否则,它们被添加为URL查询参数。

@RequestPart

添加一个 request part,它可以是一个 String(表单字段)、Resource(文件 part)、对象(要编码的实体,如JSON)、HttpEntity(part 内容和 header)、Spring Part 或上述任何一个的 Reactive Streams Publisher

@CookieValue

添加一个或多个cookie。参数可以是一个 Map<String, ?> 或 MultiValueMap<String, ?> 的多个cookie,一个 Collection<?>,或一个单独的值。对于非 String 值,支持类型转换。

2、返回值

注解式 HTTP exchange 方法支持以下返回值:

方法返回值

说明

voidMono<Void>

执行给定的请求,并释放响应内容,如果有的话。

HttpHeadersMono<HttpHeaders>

执行给定的请求,释放响应内容(如果有),并返回响应头信息。

<T>Mono<T>

执行给定的请求并将响应内容解码为声明的返回类型。

<T>Flux<T>

执行给定的请求,并将响应内容解码为声明的元素类型的流。

ResponseEntity<Void>Mono<ResponseEntity<Void>>

执行给定的请求,并释放响应内容(如果有的话),并返回一个带有状态和头信息的 ResponseEntity

ResponseEntity<T>Mono<ResponseEntity<T>>

执行给定的请求,将响应内容解码为声明的返回类型,并返回一个带有状态、头信息和解码后的 body 的 ResponseEntity

Mono<ResponseEntity<Flux<T>>

执行给定的请求,将响应内容解码为声明的元素类型的流,并返回一个带有状态、头信息和解码后的响应体流的 ResponseEntity

3、异常处理

默认情况下,WebClient 会对 4xx 和 5xx HTTP状态代码引发 WebClientResponseException。要定制这一点,你可以注册一个响应状态 handler,适用于通过客户端执行的所有响应:

WebClient webClient = WebClient.builder()
        .defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
        .build();

WebClientAdapter clientAdapter = WebClientAdapter.forClient(webClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory
        .builder(clientAdapter).build();

Spring 教程—REST 客户端详解_xml_14

关于更多的细节和选项,例如抑制错误状态代码,请参见 WebClient.Builder 中 defaultStatusHandler 的 Javadoc。

大家好,我是Doker品牌的Sinbad,欢迎点赞和评论,您的鼓励是我们持续更新的动力!欢迎加微信进入技术群聊!


标签:HTTP,String,一个,Spring,RestTemplate,REST,响应,请求,客户端
From: https://blog.51cto.com/Doker/6195708

相关文章

  • 获取客户端ip
    Nginx中添加配置:location/{#保留代理之前的host包含客户端真实的域名和端口号proxy_set_headerHost$host;#保留代理之前的真实客户端ipproxy_set_headerX-Real-IP$remote_addr;#这个Header和X-Real-IP类似,但它在......
  • 不同版本的Spring Framework有哪些主要功能?
    官方地址:https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions SpringFramework版本 JDKJavaEE/JakartaEESpringBoot支持新特性eof6.x6.0.xJDK 17-21JakartaEE9-10Springboot3.x What'sNewinSpring......
  • Java SpringBoot 7z 压缩、解压
    JavaSpringBoot7z压缩、解压cmd7z文件压缩7z压缩测试添加依赖<dependency><groupId>org.apache.commons</groupId><artifactId>commons-compress</artifactId><version>1.12</version></dependency><dependency......
  • Springboot使用RestTemplate发送Post请求postForEntity (application/json)的坑
    当使用RestTemplate进行http请求时,的确很方便,但是当需要进行post请求时遇到了坑1POST传递参数:采用LinkedMultiValueMap,不能使用HashMapStringurl='http://posturl';MultiValueMap<String,String>map=newLinkedMultiValueMap<String,String>();map.add(......
  • 深谈Spring如何解决Bean的循环依赖
    1.什么是循环依赖Java循环依赖指的是两个或多个类之间的相互依赖,形成了一个循环的依赖关系,这会导致程序编译失败或运行时出现异常。下面小岳就带大家来详细分析下Java循环依赖。简单来讲就是:假设有两个人是:A和B,A想要向B借钱,但B需要先向A借钱。这种情况就形成了循环依赖关系,无......
  • springboot整合swagger2
     1.正文1.1什么是swagger2Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务的接口文档 .接口:controller相应的路径方法Swagger2是一款前后端分离开发中非常实用的API管理工具,它可以帮助开发者根据约定规范自动生成API文档,并支持......
  • Spring AOP官方文档学习笔记(二)之基于注解的Spring AOP
    1.@Aspect注解(1)@Aspect注解用于声明一个切面类,我们可在该类中来自定义切面,早在Spring之前,AspectJ框架中就已经存在了这么一个注解,而Spring为了提供统一的注解风格,因此采用了和AspectJ框架相同的注解方式,这便是@Aspect注解的由来,换句话说,在Spring想做AOP框架之前,AspectJAOP框......
  • spring事务传播
    采用编程式事务1、getCurrentSession()与openSession()的区别?*采用getCurrentSession()创建的session会绑定到当前线程中,而采用openSession()创建的session则不会*采用getCurrentSession()创建的session在commit或rollback时会自动关闭,而采用openSession创建的session必须手......
  • spring工具类
    文件资源操作:org.springframework.core.io.Resource接口,是为了统一各种类型的资源而定义的1.访问文件资源org.springframework.core.io.ClassPathResource类路径进行访问org.springframework.core.io.FileSystemResource文件系统的绝对路径进行访问org.springframework.web.conte......
  • memcache的客户端
    Memcached的java客户端已经存在三个了:官方提供的基于传统阻塞io的客户端 、DustinSallings实现的基于javanio的spymemcached和XMemcached。memcache常用Java客户端有三个:1、memcachedclientforJava  2、spymemcached 3、xmemcachedxmemcached上述三者中最优秀的。官网......