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

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

为什么要使用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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/279899.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

译⽂:Top Three Use Cases for Dapr and Kubernetes

有关译者&#xff1a;陈东海(seachen)&#xff0c;⽬前就职于腾讯&#xff0c;同时在社区也是⼀名Dapr Member.导语&#xff1a;在SDLC(Software Development Lifecycle软件开发⽣命周期中)&#xff0c;绝⼤多数CNCF项⽬都是专注于软件开发的中后期阶段&#xff0c;特别是运维和…

MySQL数据库的datetime与timestamp

MySQL数据库中有datetime与timestamp两种日期时间型数据类型&#xff0c;其中timestamp可以用timestamp(n)来表示年月日时分秒的取值精度&#xff0c;如果n14则完整匹配于datetime的精度&#xff0c;那为什么还需要datetime这种类型呢&#xff1f;我做过试验&#xff0c;timest…

平视相机svo开源项目_什么是平视显示器(HUD),我应该得到一个吗?

平视相机svo开源项目In a world full of augmented reality snowboard goggles and Google Glass, it seems only fair that our cars get to enjoy some of the same treatment. Heads-up displays, or “HUDs” as they’re better known, are a new type of add-on for cons…

yum 下载RPM包而不进行安装

yum命令本身就可以用来下载一个RPM包&#xff0c;标准的yum命令提供了--downloadonly(只下载)的选项来达到这个目的。 $ sudo yum install --downloadonly <package-name> 默认情况下&#xff0c;一个下载的RPM包会保存在下面的目录中: /var/cache/yum/x86_64/[centos/fe…

react项目打包后路径找不到,项目打开后页面空白的问题

使用 npm install -g create-react-app快速生成项目脚手架打包后出现资源找不到的路径问题&#xff1a; 解决办法&#xff1a;在package.json设置homepage 转载于:https://www.cnblogs.com/lan-cheng/p/10541606.html

linux 下实现ssh免密钥登录

小伙伴经常在运维的时候需要ssh到很多其他的服务器&#xff0c;但是又要每次输入密码&#xff0c;一两台还没什么&#xff0c;多了就烦了。所以这里教大家如何直接ssh到其他机器而不用输入密码。[rootjw ~]# ssh-keygen -t rsaGenerating public/private rsa key pair.Enter fi…

一些部署django用到的linux命令

mv untitled45/ /1601F/wang/ 将XXXX移动到XXX&#xff0c;也可以用于给XXX重新命名 zip -r -q -o hello.zip /1601F/3/untitled45 安静的递归压缩成zip文件 gunicorn -w 3 -b 0.0.0.0:8080 untitled45.wsgi:application启动项目&#xff08;需要在manage.py同级目录下运行&am…

ios 拍照 实现 连拍_如何在iOS设备上使用连拍模式拍照

ios 拍照 实现 连拍We’re sure you’ve tried to capture that perfect moment with your camera and you’re just a tad too late or too early and you miss it. If you own an iPhone or iPad, you can use burst mode and never miss that perfect shot again. 我们确定您…

pta l2-6(树的遍历)

题目链接&#xff1a;https://pintia.cn/problem-sets/994805046380707840/problems/994805069361299456 题意&#xff1a;给出一个二叉树的结点数目n&#xff0c;后序遍历序列post&#xff0c;中序遍历序列in&#xff0c;求其层序遍历序列。 思路&#xff1a;首先给二叉树每个…

路由热备份(HSRP)DynamipsGUI小试牛刀

——好久不见啊&#xff0c;大家最近过的还好吗&#xff1f;——学而不思则罔&#xff0c;思而不学则殆。好了&#xff0c;既然已经踏上了CCNP之旅&#xff0c;那就和大家一起分享一下学习HSRP的体会吧——在CCNA中我们设计网络的目的主要是——通&#xff01;到了CCNP&#xf…

WPF 如何实现简单放大镜

WPF 如何实现简单放大镜控件名&#xff1a;Magnifier作 者&#xff1a;WPFDevelopersOrg - 驚鏵原文链接[1]&#xff1a;https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用.NET40&#xff1b;Visual Studio 2019;实现此功能需要用到 VisualBrush &#xff0c;放大镜…

input 禁用智能提示_如何在智能手机上禁用紧急警报

input 禁用智能提示AMBER and emergency alerts occur when there’s a child abduction or there’s an important event such as a severe weather alert (tornado warning) that local governments needs to make people aware of. While we don’t recommend disabling the…

laravel中使用的PDF扩展包——laravel-dompdf和laravel-snappy

这两天项目中需要将HTML页面转换为PDF文件方便打印&#xff0c;我在网上搜了很多资料。先后尝试了laravel-dompdf和laravel-snappy两种扩展包&#xff0c;个人感觉laravel-snappy比较好用。 一、使用laravel-dompdf扩展包 1、安装扩展包 我们通过composer来安装 composer requi…

「读懂源码系列2」我从 lodash 源码中学到的几个知识点

前言 上一篇文章 「前端面试题系列8」数组去重(10 种浓缩版) 的最后&#xff0c;简单介绍了 lodash 中的数组去重方法 _.uniq&#xff0c;它可以实现我们日常工作中的去重需求&#xff0c;能够去重 NaN&#xff0c;并保留 {...}。 今天要讲的&#xff0c;是我从 _.uniq 的源码实…

有小伙伴问:上位机用QT还是winform/wpf好?

楔子有小伙伴问&#xff1a;上位机用QT还是winform/wpf好&#xff1f;Qt是C写的&#xff0c;跨平台的UI框架&#xff0c;Winform/wpf是C#写的不跨平台的Windows上运行的UI框架。这两个说到底是语言本质的争论或者区别。优点Qt的优点是可以跨平台运行UI界面&#xff0c;在Linux&…

使用jenkins进行项目的自动构建部署

jenkins 简介 Jenkins是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;功能包括&#xff1a;持续的软件版本发布/测试项目和监控外部调用执行的工作。 官网地址地址&#xff1a; https://jenkins.io 下载安装启动 CentOS 下用yum进行安装启动 …

如何删除Apple Music中的连接功能

Love Apple Music, but tired of the intrusive Connect feature taking up space on your favorite artist’s page? Well, don’t worry, because getting “dis-Connected” is just a matter of changing a few simple settings in your iPhone or iPad running iOS 8.0 o…

python设计模式(十四):模板方法模式

定义一个算法或者流程&#xff0c;部分环节设计为外部可变&#xff0c;用类似于模板的思想来实例化一个实体&#xff0c;可以往模板中填充不同的内容&#xff1b;在模板思想下&#xff0c;实体的整体框架是确定的&#xff0c;他是一个模板&#xff0c;但是模板下内容可变&#…

FirstBird--项目流程

创建项目(英文路径)—–img图片文件创建窗体–设置大小(Basic—size–>320*480)—最大化功能禁用(Expert–>setResizable(false))添加面板–设置布局方式(set Layout—>AbsoluteLayout)自己创建面板 GameMain中将Jpanel1改为WinJpanel–创建对应类–>extends JPane…