为什么要使用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请求。如果上面的请求没有找到对应的方法,可以使用更加通用的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