←←←←←←←←←←←← 快!点关注

为什么要使用RestTemplate?

随着微服务的广泛使用,在实际的开发中,客户端代码中调用RESTful接口也越来越常见。在系统的遗留代码中,你可能会看见有一些代码是使用HttpURLConnection来调用RESTful接口的,类似于下面这样:

URL url = ...
// 打开连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
try {conn.setRequestMethod("POST");conn.setDoInput(true);conn.setDoOutput(true);conn.connect();// 发送数据...BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream(), "utf-8"));bw.write(str);// 接收数据 ...BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));String line = null;while ((line = br.readLine()) != null) {...}
} finally {conn.disconnect();
}
复制代码

从上面的代码可以看出,使用HttpURLConnection调用RESTful接口是比较麻烦的,假如要调用30个接口,每个接口都使用类似于上面的代码 进行调用,那简直是一场灾难(写这么多无聊的样板代码,内心绝对是崩溃的)。有人可能会想,将常用的RESTful操作(例如GET、POST、DELETE)封装成工具类,再调用不是也可以吗!这样做确实可行,但是要封装成通用的工具类不是那么简单的(需要一定的经验)。调用RESTful接口,还有另外一种选择:Apache HttpComponents。虽然使用它发送HTTP请求确实挺方便的,但是使用它调用RESTful接口好像也挺麻烦的!

直到我遇到了RestTemplate,世界顿时都明亮了,腰也不酸了,腿也不疼了,一下子就对接好了10个RESTful接口。写的代码可能变成是这样子的:

RestTemplate template = ...
// 请求地址
String url = ...
// 请求参数
UserParams request = ...
// 执行POST请求
User u = template.postForObject(url, request, User.class);
...
复制代码

上面的调用代码变的简洁了很多,是不是很爽啊!RestTemplate是spring提供用来调用RESTful接口的类,里面提供了大量便捷的方法,如下:

执行不同的请求,只需找到这种请求方式所对应的方法就行,上例中的postForObject就是发送的POST请求。如果上面的请求没有找到对应的方法,可以使用更加通用的exchangeexecute方法。

RestTemplate的可扩展性也很强(下面列出比较常用的几种方式):

  1. RestTemplate默认使用的是JDK中的HttpURLConnection实现的,如果你想要切换到其他的HTTP库,例如,Apache HttpComponents, Netty和OkHttp,只需要调用setRequestFactory方法来进行设置,甚至可以使用自己实现的类型,只需要继承自ClientHttpRequestFactory。(一般情况下,使用默认的实现就好)
  2. RestTemplate默认使用的是DefaultResponseErrorHandler响应错误处理器,你可以调用setErrorHandler来定制自己的响应错误处理器。
  3. 客户端请求拦截器:ClientHttpRequestInterceptor,实现这个接口,并在org.springframework.web.client.RestTemplate#setInterceptors(java.util.List)中进行注册,能对请求头和请求体的内容进行修改,并能对响应的内容进行修改。(下面将会讲解)
  4. RestTemplate中的doExecute方法,所有请求方式最终都会执行这个方法,重写此方法能完成一些必要的操作。

RestTemplate的妙用

考虑这样一个场景:假如说A部门开发的用户相关的微服务接口,提供给B部门来使用,在使用时需要在请求头中加入基本的认证头信息,认证头的格式如下:

Authorization=Basic {token}
复制代码

token的格式是:base64(username:password)。假如username是zfx,密码是123,结果是先拼接用户名和密码:zfx:123,再用utf-8格式获取其字节码,进行base64编码的结果为:emZ4OjEyMw==

假如要调用A部门的接口,根据id来获取用户的信息,代码如下:

String userId = "11";
String url = "http://127.0.0.1:8080/v1/users/{id}";RestTemplate restTemplate = new RestTemplate();// 基本的认证头信息
String username  = "zfx";
String password = "123";
String token = Base64Utils.encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + token);
HttpEntity<Void> requestEntity = new HttpEntity<>(headers);ResponseEntity<User> exchange = restTemplate.exchange(url, HttpMethod.GET, requestEntity, User.class, userId);
User result = exchange.getBody();
复制代码

首先先创建RestTemplate的实例,在实际的开发中,最好不要每次都创建RestTemplate的实例,最好在spring中以单例的方式来配置,要使用的地方直接注入。用base64来编码了用户名和密码,然后在请求头中进行设置。restTemplate.exchange执行请求,并获取返回的结果。上面的代码其实并不难,但是这样写会不会有啥问题呢!假如说,叫你对接A部门的根据机构id来获取机构信息的接口,你岂不是又要把上面的代码再重新复制一下吗?这样不是很麻烦,代码会很臃肿。

spring提供了ClientHttpRequestInterceptor这个类,适合用来处理这种情况,加入基本的认证头信息,可以使用spring提供的这个类:BasicAuthorizationInterceptor。使用配置的方式来配置RestTemplate:

@Configuration
public class RestTemplateConfig {@Value("${zfx.username}")private String username;@Value("${zfx.password}")private String password;@Bean("basicAuthRestTemplate")public RestTemplate createBasicAuthRestTemplate() {RestTemplate restTemplate = new RestTemplate();List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();interceptors.add(new BasicAuthorizationInterceptor(username, password));restTemplate.setInterceptors(interceptors);return restTemplate;}}
复制代码

上面的代码在创建basicAuthRestTemplate时,会加入基本的认证头信息的拦截器,来设置基本认证头信息。

再次调用上面的接口时,代码可以这样写:

@Autowired
RestTemplate basicAuthRestTemplate;
...
String userId = "11";
String url = "http://127.0.0.1:8080/v1/users/{id}";
User result = basicAuthRestTemplate.getForObject(url, User.class, userId);
复制代码

代码一下子简洁了这么多,假如说要调用根据机构id来获取机构信息的接口呢?如下:

@Autowired
RestTemplate basicAuthRestTemplate;
...
String orgId = "11";
String url = "http://127.0.0.1:8080/v1/orgs/{id}";
Org result = basicAuthRestTemplate.getForObject(url, Org.class, orgId);
复制代码

代码一下子简洁了很多,对接这些接口的程序员不用再关心认证是怎么一回事,认证这件事对于他们完全是透明的,他们只需要专注于编写他们自己的逻辑代码就可以了。ClientHttpRequestInterceptor的实现也很简单,代码如下:

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,ClientHttpRequestExecution execution) throws IOException {String token = Base64Utils.encodeToString((this.username + ":" + this.password).getBytes(StandardCharsets.UTF_8));request.getHeaders().add("Authorization", "Basic " + token);return execution.execute(request, body);
}
复制代码

在intercept方法中加入了基本的认证头信息。

假如说,有一天认证方式变了,改成OAuth2.0了,那么我们只需要实现自己的请求拦截器就行了,如下:

public class BearerAuthorizationInterceptimplements ClientHttpRequestInterceptor {@Overridepublic ClientHttpResponse intercept(HttpRequest request, byte[] body,ClientHttpRequestExecution execution) throws IOException {String token = 这些写获取token的逻辑;request.getHeaders().add("Authorization", "Bearer " + token);return execution.execute(request, body);}}
复制代码

然后配置restTemplate:

    @Bean("bearerAuthRestTemplate")public RestTemplate createBearerAuthRestTemplate() {RestTemplate restTemplate = new RestTemplate();List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();interceptors.add(new BearerAuthorizationIntercept());restTemplate.setInterceptors(interceptors);return restTemplate;}
复制代码

那么只需要注入bearerAuthRestTemplate,就能使用他了:

@Autowired
RestTemplate bearerAuthRestTemplate;
...
String userId = "11";
String url = "http://127.0.0.1:8080/v1/users/{id}";
User result = bearerAuthRestTemplate.getForObject(url, User.class, userId);
复制代码

转载于:https://juejin.im/post/5c8cba09e51d453e2762de2f

【本人秃顶程序员】技巧分享丨spring的RestTemplate的妙用,你知道吗?相关推荐

  1. 【本人秃顶程序员】分享一些数据结构与算法常用的算法技巧总结

    今天和大家讲讲,在做算法题时常用的一些技巧.对于平时没用过这些技巧的人,或许你可以考虑试着去看看在实践中能否用的上这些技巧来优化问题的解. 一.巧用数组下标 数组的下标是一个隐含的很有用的数组,特别是 ...

  2. 【本人秃顶程序员】美女程序员观点:程序员最重要的非编程技巧

    ←←←←←←←←←←←← 快!点关注 这是来自一位美女程序员Ali Spittel的观点,至少可以看看美女喜欢和怎样的男程序猿打交道: 当我想与我希望的程序员合作时,我更多地考虑非技术技能,而不是技术 ...

  3. 【本人秃顶程序员】我简历上的Java项目都好low,怎么办?

    ←←←←←←←←←←←← 快!点关注 这篇文章我们来聊一聊,在系统设计和项目经验这两块,应该如何充分的准备,才能拿出有技术含量的项目经验战胜跟你同台竞技的其他工程师,征服你的面试官,收获各种心仪的of ...

  4. 【本人秃顶程序员】使用Spring Cloud Stream和RabbitMQ实现事件驱动的微服务

    ←←←←←←←←←←←← 快!点关注 让我们展示如何使用Spring Cloud Stream来设计事件驱动的微服务.首先,Spring Cloud Stream首先有什么好处?因为Spring AM ...

  5. java程序员秃顶,【本人秃顶程序员】使用Azure Function + Cognitive Services 实现图片自动化审核...

    ←←←←←←←←←←←← 快!点关注 假定我们正在运行某个应用程序,此应用程序需要用户在应用程序中提交大量图片文件,那么对于系统管理员来说手动审核这些图片是很消耗时间的,并且对于图片的审核也许并不是即 ...

  6. 【本人秃顶程序员】图解分布式架构的演进过程

    ←←←←←←←←←←←← 快!点关注!!! 一.什么是分布式架构 分布式系统(distributed system) 是建立在网络之上的软件系统. 内聚性: 是指每一个数据库分布节点高度自治,有本地的 ...

  7. 【本人秃顶程序员】高级 Java 必须突破的 10 个知识点!

    ←←←←←←←←←←←← 快!点关注!!! 工作多少年了,还在传统公司写if / for 等简单的代码?那你就真的要被社会淘汰了,工作多年其实你与初级工程师又有多少区别呢?那么作为一个高级Java攻城 ...

  8. 【本人秃顶程序员】Redis 这么火,它都解决了哪些问题?

    ←←←←←←←←←←←← 快!点关注 先看一下Redis是一个什么东西.官方简介解释到: Redis是一个基于BSD开源的项目,是一个把结构化的数据放在内存中的一个存储系统,你可以把它作为数据库,缓存 ...

  9. 【本人秃顶程序员】求求你别再写 bug 了,秃顶程序员告诉你避免空指针的 5 个案例!

    ←←←←←←←←←←←← 快!点关注 空指针是我们 Java 开发人员经常遇到的一个基本异常,这是一个极其普遍但似乎又无法根治的问题. 本文,栈长将带你了解什么是空指针,还有如何有效的避免空指针. 什 ...

最新文章

  1. PHP中魔术方法的用法
  2. eclipse lombok插件安装_便捷开发,解放双手,lombok插件
  3. nginx upstream配置_Prometheus快速监控Nginx
  4. 高效程序员应该养成的七个习惯
  5. c中获取python控制台输出_在真实的tim中用C捕获控制台python打印
  6. printdocument python_python学习笔记之wxpython打印预览
  7. java swing 代码_java swing编写gui生命游戏代码,新手上路
  8. [译] JavaScript 的函数式编程是一种反模式
  9. java停机保存数据_哦,这就是java的优雅停机?(实现及原理)
  10. dbf文件mysql,dbf文件怎么打开?dbf是什么文件?
  11. Sharepoint Portal Server 2005?
  12. Android设计模式--观察者模式
  13. paip.svn不能忽略文件的bug.txt
  14. Java400道面试题通关宝典助你进大厂,赶紧收藏起来!
  15. 文字转换片假字_模仿文字转换笔迹,word手写字体在线生成器网站
  16. Java工作5年的迷茫,是否要转互联网?
  17. Java并发编程--线程池ThreadPollExecutor原理探究
  18. NAS如何进行磁盘碎片整理?
  19. 安卓沉浸式状态栏_安卓平板也能有品质感,小新Pad Pro上手
  20. python所用到的英语单词_用Python自制考研英语单词手册

热门文章

  1. pythonif语句的多分支使用_Python多分支if语句的使用
  2. 用JS轻松实现一个录音、录像、录屏工具库
  3. 面试官问:JS的继承
  4. 易思汇完成近亿元B轮融资,信中利投资
  5. 9.28PMP每日一题
  6. Vue 教程第一篇——基础概念
  7. 手把手教你做产品经理,视频课教程已经发布,欢迎观看
  8. ​ArduinoYun教程之ArduinoYun硬件介绍
  9. php 转码iconv,PHP iconv()函数转字符编码的问题(转)
  10. 乘基取整法是什么_十进制小数转二进制小数乘2取整法的直观理解