转载

Spring Boot教程(24) – 用RestTemplate访问外部服务

日常开发中难免会向应用外部发起HTTP请求,比如访问云存储平台的API、调用微信或支付宝的API、抓取网页等等。Spring框架提供了 RestTemplate 来完成这一需求。 RestTemplate 提供了很多方法,方便你发起GET和POST等请求。下图是个简单的调用,去获取网页的HTML代码:

Spring Boot教程(24) – 用RestTemplate访问外部服务

getForEntity方法会发起GET请求,然后返回一个 ResponseEntity 对象,它把HTTP请求的响应抽象成一个对象。通过 ResponseEntity ,你可以获取到此次响应的状态码、首部(Header)和主体(Body)。有的时候你仅仅需要Body部分,那就可以使用getForObject方法。

Spring Boot对 RestTemplate 也做了自动配置,但是他并没有提供给一个全局的 RestTemplate 对象,而是给了个全局的 RestTemplateBuilder 对象,你可以在需要的时候,build一个 RestTemplate 出来,像下面一样:

Spring Boot教程(24) – 用RestTemplate访问外部服务

Spring Boot在创建了 RestTemplateBuilder 之后,还给他配置了很多 HttpMessageConverter ,它的作用是把HTTP请求中的Body转化为Java中的对象,或者把对象转化为Body。我们看看上面的getForObject方法,第二个参数是个Class对象,告诉 RestTemplate 你想把Body转化成什么类型的对象,如果你传递了String.class,那么 StringHttpMessageConverter 就会将Body转换成String对象。如果你传递了byte[].class,那么 ByteArrayHttpMessageConverter 会把Body转化成byte[]对象,如果你传递了某个JSON映射类, MappingJackson2HttpMessageConverter 会把Body转化成这个映射类的对象。更多相关信息,你可以查看 HttpMessageConvertersAutoConfigurationRestTemplateAutoConfiguration 的源码。

我们再来看看RestTemplate所提供的一些方法:

Spring Boot教程(24) – 用RestTemplate访问外部服务
GET请求
Spring Boot教程(24) – 用RestTemplate访问外部服务
POST请求
Spring Boot教程(24) – 用RestTemplate访问外部服务
DELETE请求

除了上面三张图中的GET、POST和DELETE方法, RestTemplate 还有一些类似的方法来发起HEAD、OPTIONS、PUT、PATCH等请求。从这些方法的命名和参数可以看出, RestTemplate 的风格很RESTful,如果你访问的网络服务的API是这种风格的话,那么使用起来会非常顺手。

底层HTTP库

RestTemplate 是比较高阶的接口,它利用了底层的HTTP库,底层的HTTP库有多种选择:

  • Apache HttpComponents
  • java.net.HttpURLConnection
  • OkHttp

Spring为了方便你在这些这些库之间进行切换,实现了不同的 ClientHttpRequestFactory ,你选择了不同的库,就需要给 RestTemplate 设置不同的 ClientHttpRequestFactory ,上面三种库分别对应下面三个类:

  • HttpComponentsClientHttpRequestFactory
  • SimpleClientHttpRequestFactory
  • OkHttp3ClientHttpRequestFactory
Spring Boot教程(24) – 用RestTemplate访问外部服务

当你需要的时候,可以创建一个 ClientHttpRequestFactory 对象,传给 RestTemplate 。当然,一般情况下,你不必像上图这么做。Spring Boot会看看你的类路径下有没有HttpComponents,有的话就用,没有的话看看有没有OkHttp,最后才是保底的HttpUrlConnection。如果你想使用OkHttp,可以直接在build.gradle里加上依赖,其他啥都不用做。

值得一提的是,OkHttp对应的是 OkHttp3ClientHttpRequestFactory ,但是目前OkHttp的版本已经到4.x了,是不是应该再找一个 OkHttp4ClientHttpRequestFactory 呢?其实4.x版本只是用Kotlin把OkHttp重新写了一遍,接口并没有改变,还是兼容3.x的。另外你可能要问,我项目用的是Java,OkHttp用的是Kotlin,是不是我就不能在项目里用了呀?其实不是,虽然你在Intellij IDEA里跳入OkHttp源码时看到的是Kotlin代码,但是实际项目编译的时候,你引入的是Kotlin编译过之后的class文件,所以说加入4.x版本的OkHttp照样能用。

URI模板

在给 RestTemplate 传递链接的时候,可以给链接里设置占位符,方便之后通过它来动态生成链接。下图中,链接中的”userId”占位符的位置之后会被替换成10。

Spring Boot教程(24) – 用RestTemplate访问外部服务

我刚开始没有研究API的时候,发现getForEntity方法的第三个参数是Map类型,自然而然地以为它是用来传递请求参数的(request parameter或者说query parameter),后来运行的时候才发现不对。如果你想传递请求参数,那么你可能需要使用 UriComponentsBuilder 来修改链接。

自定义Header

如果你想给请求自定义Header,直接用getForObject或者postForObject这种方法可能做不到。这个时候,就需要使用更加通用的exchange方法:

Spring Boot教程(24) – 用RestTemplate访问外部服务

一个HTTP请求,说白了也就这4个东西需要设定:HTTP方法、URL、Header和Body。exchange的这么多种重载方法就是变着花样方便你去传递这四个东西。你在上面这些方法中可能没直接看到设置Header的地方,因为Header被包含在 HttpEntity 类中, HttpEntity = HttpHeaders + Body 。另外,你还可以使用 RequestEntity 去构造一个请求。 RequestEntity 继承于 HttpEntity ,相当于 RequestEntity = HttpEntity + url + HTTP方法

Spring Boot教程(24) – 用RestTemplate访问外部服务

如果你想给每个请求都加上特定的Header,可以通过拦截器实现:

Spring Boot教程(24) – 用RestTemplate访问外部服务

我在写上面的代码的时候发现了一个巨坑。 RestTemplateBuilder 每次设定过之后,会返回一个新的 RestTemplateBuilder ,也就是说,在上图中,builder和newBuilder不是同一个对象,这好像跟我们以前所接触过的各种Builder不太一样,以前的Builder每次设置了之后都会返回this,而 RestTemplateBuilder 会去new一个新的对象,刚开始我感觉不太合理,没必要这么做。后来想想,因为Spring Boot会默认提供一个全局的 RestTemplateBuilder ,如果一个类对它进行了修改,其他类获取 RestTemplateBuilder 的时候就是修改过的了,可能会产生副作用。

上面我们说了给 单个 RestTemplate 的所有请求都添加Header的方法,下面说说给 所有 RestTemplate 添加Header的方法。 RestTemplateCustomizer 是用来对 RestTemplate 进行修改的类,我们在容器中扔这样一个 RestTemplateCustomizer 对象,那么Spring Boot在创建 RestTemplateBuilder 的时候,会自动把它提供给 RestTemplateBuilder ,这样每个 RestTemplate 都可以自定义了:

Spring Boot教程(24) – 用RestTemplate访问外部服务

虽然上面我们的例子写的是给请求添加Header,但是你可以做的更多,总的来说,就是框架给了你一种能力:可以对单个请求、单个 RestTemplate 、以及所有 RestTemplate 进行自定义。我们可以看出, RestTemplate 的设计还是非常灵活方便的。

异常处理

默认情况下,遇到4xx错误,或者5xx错误的时候,会抛出异常,不同的状态码有不同的异常,可能的异常如下图:

Spring Boot教程(24) – 用RestTemplate访问外部服务

捕获异常之后,你还可以从异常对象中获取状态码、Header和Body,以便根据不同的错误信息做不同的处理。如果你对默认的异常处理机制不满意,可以自定义一个 ResponseErrorHandler 传递给 RestTemplate ,不过我觉得默认的就挺好,没必要再搞一套。

最后

本文介绍了日常开发可能用到的接口和场景,还有一些比较细节的东西还可以挖掘,比如通过Multipart请求上传文件等等。除了 RestTemplate ,你还可以选择 WebClient 这种非阻塞、响应式的请求工具,它通常和WebFlux一起使用。另外你还可以用 Retrofit 和 Feign 来通过编写接口方法+注解来定义HTTP请求,使用起来非常简单和容易,我个人很喜欢这种工具,接下来肯定会写一篇文章来总结他们的用法的。

原文  https://fookwood.com/spring-boot-tutorial-24-resttemplate
正文到此结束
Loading...