还不知道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
。关于如何调用,可以参考我之前写过的一篇文章:学习HttpClient,从两个小例子开始。虽然使用它发送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请求。如果上面的请求没有找到对应的方法,可以使用更加通用的exchange
和execute
方法。
RestTemplate的可扩展性也很强(下面列出比较常用的几种方式):
- RestTemplate默认使用的是JDK中的HttpURLConnection实现的,如果你想要切换到其他的HTTP库,例如,Apache HttpComponents, Netty和OkHttp,只需要调用
setRequestFactory
方法来进行设置,甚至可以使用自己实现的类型,只需要继承自ClientHttpRequestFactory
。(一般情况下,使用默认的实现就好) - RestTemplate默认使用的是
DefaultResponseErrorHandler
响应错误处理器,你可以调用setErrorHandler
来定制自己的响应错误处理器。 - 客户端请求拦截器:
ClientHttpRequestInterceptor
,实现这个接口,并在org.springframework.web.client.RestTemplate#setInterceptors(java.util.List)
中进行注册,能对请求头和请求体的内容进行修改,并能对响应的内容进行修改。(下面将会讲解) - 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 BearerAuthorizationIntercept
implements ClientHttpRequestInterceptor {
@Override
public 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);
还不知道spring的RestTemplate的妙用吗的更多相关文章
- 听说你还不知道Spring是如何解决循环依赖问题的?
Spring如何解决的循环依赖,是近两年流行起来的一道Java面试题. 其实笔者本人对这类框架源码题还是持一定的怀疑态度的. 如果笔者作为面试官,可能会问一些诸如"如果注入的属性为null, ...
- 难道你还不知道Spring之事务的回滚和提交的原理吗,这篇文章带你走进源码级别的解读。
上一篇文章讲解了获取事务,并通过获取的connection设置只读,隔离级别等:这篇文章讲事务剩下的回滚和提交. 事务的回滚处理 之前已经完成了目标方法运行前的事务准备工作.而这些准备工作的最大目的无 ...
- 对Spring 的RestTemplate进行包装
Spring的RestTemplate及大地简化了REST Client的开发,但每次还要编写大量的模板代码,代码不够简洁.我对他进行了一次包装,采用接口来声明REST接口,使用Annotation对 ...
- Spring’s RestTemplate
Spring’s RestTemplate /** * After the word document is generated in memory we can upload it to the s ...
- 你还不知道Vue的生命周期吗?带你从Vue源码了解Vue2.x的生命周期(初始化阶段)
作者:小土豆biubiubiu 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.im/user/58c61b4361ff4b005d9e8 ...
- JDK15就要来了,你却还不知道JDK8的新特性!
微信搜「烟雨星空」,白嫖更多好文. 现在 Oracle 官方每隔半年就会出一个 JDK 新版本.按时间来算的话,这个月就要出 JDK15 了.然而,大部分公司还是在使用 JDK7 和 8 . 之前去我 ...
- Spring AOP应用场景你还不知道?这篇一定要看!
回顾一下Spring AOP的知识 为什么会有面向切面编程(AOP)? 我们知道Java是一个面向对象(OOP)的语言,但它有一些弊端,比如当我们需要为多个不具有继承关系的对象引入一个公共行为,例如日 ...
- Spring 最常用的 7 大类注解,哪些你还不知道?
随着技术的更新迭代,Java5.0开始支持注解.而作为java中的领军框架spring,自从更新了2.5版本之后也开始慢慢舍弃xml配置,更多使用注解来控制spring框架. 而spring的的注解那 ...
- Spring中RestTemplate的使用方法
一.REST 在互联网中,我们会通过请求url来对网络上的资源做增删改查等动作,这里的请求包含两部分:动词,主要包括增.删.改.查:名词,就是网络中的各种资源.传统的非REST风格的请求方式是把动词和 ...
随机推荐
- PHP之道 - php各方面的知识汇总
看到一个PHP的知识各方面的汇总,写的很有借鉴意义,搬过来了 转自: https://laravel-china.github.io/php-the-right-way/ 欢迎阅读 其他语言版本 参与 ...
- dubbo和dubboX与微服务架构(dubbo一)
一.传统三层架构模式的缺陷 三层架构(3-tier architecture) 通常意义上的三层架构就是将整个业务应用划分为:界面层(User Interface layer)web.业务逻辑层(Bu ...
- Spark源码剖析 - SparkContext的初始化(一)
1. SparkContext概述 注意:SparkContext的初始化剖析是基于Spark2.1.0版本的 Spark Driver用于提交用户应用程序,实际可以看作Spark的客户端.了解Spa ...
- [物理学与PDEs]第1章第1节 引言
1. 电动力学研究的对象是电磁场, 研究电磁场的基本属性---运动规律及它和带电物质的相互作用. 2. 场, 物质的一种存在方式. 3. Maxwell 方程组是电动力学中的基本方程, 是一切有关电磁 ...
- 在vue中引入scss
先npm安装stylus和stylus-loader (我安装后,报错提示缺少stylus,所以第一步安装stylus,如果没有提示可省略第一步) 1.到 package.json ----- de ...
- v-model
仅用于以下控件: <input> <select> <textarea> 组件 v-model以Vue 实例的数据作为数据来源,应当在组件的 data 选项中声明初 ...
- Java 计算两个日期相差月数、天数
package com.myjava; import java.text.ParseException; import java.text.SimpleDateFormat; import java. ...
- [Kubernetes]编排其实很简单
什么是编排 Kubernetes中,我们总是在说一个概念:编排. 在[Kubernetes]谈谈Kubernetes的本质这篇文章中,关于"编排"的概念介绍了一下:过去很多集群管理 ...
- anaconda3安装cv2模块(python3.6)
- Python-Django-BMS-增删改查
无名分组 有名分组 反向解析 更改路由后不影响,动态传值 locals()传参 queryset.update() 自定义过滤器 {{forloop.counter}} new.authors.add ...