对四年前的文章重新做了下修改
简介
这是由 Spring
提供的网络请求工具,它内嵌在 spring-web
中
<dependency>
<groupId></groupId>
<artifactId>spring-web</artifactId>
<version>5.3.12</version>
</dependency>
当然我们常用的 spring-boot-starter-web
也是包含它的,所以就不需要像这里一样显式引用了,其他细节可参见 RestTemplate 官方文档
使用方法
注意事项
RestTemplate
默认行为,如果请求出去未返回 200 的 http 状态码,会抛出异常,我们通常希望抛出异常交由请求方自行决定,此时我们需要覆盖默认的 ResponseErrorHandler ,下面为用什么也不干覆盖默认处理机制
RestTemplate restTemplate = new RestTemplate();
ResponseErrorHandler responseErrorHandler = new ResponseErrorHandler() {
@Override
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
return true;
}
@Override
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
}
};
(responseErrorHandler);
便捷 API
// 此处含义:向 url 发送 Get 类型请求,将请求返回的结果反序列化成 String 类型
(url, );
其他 postForObject
等类似换汤不换药 API 也是如此,通常我们不去使用,它们不够*,无法设置编码格式等,乱码等问题无法解决,不再赘述,后面只将通用方案
通用方案
()
它包含一个 HttpMethod
的入参,该参数决定了我们的请求是 Get
、Post
…… 中的哪一种
画外音: 顺便提一嘴原生的 RestTemplate
其实并不支持 Patch
类型的请求,可以引入第三方实现来扩展 RestTemplate
功能
示例
application/json 请求
RestTemplate restTemplate = new RestTemplate();
String url = "";
/* 注意:必须 http、https……开头,不然报错,浏览器地址栏不加 http 之类不出错是因为浏览器自动帮你补全了 */
HttpHeaders headers = new HttpHeaders();
/* 这个对象有add()方法,可往请求头存入信息 */
(MediaType.APPLICATION_JSON_UTF8);
/* 解决中文乱码的关键 , 还有更深层次的问题 关系到 StringHttpMessageConverter,先占位,以后补全*/
HttpEntity<String> entity = new HttpEntity<String>(body, headers);
/* body是Http消息体例如json串 */
(url, , entity, );
/*上面这句返回的是往 url发送 post请求 请求携带信息为entity时返回的结果信息 是可以修改的,其实本质上就是在指定反序列化对象类型,这取决于你要怎么解析请求返回的参数*/
application/x-www-form-urlencoded 请求
RestTemplate restTemplate=new RestTemplate();
/* 注意:必须 http、https……开头,不然报错,浏览器地址栏不加 http 之类不出错是因为浏览器自动帮你补全了 */
String url="";
String bodyValTemplate = "var1=" + ("测试数据1", "utf-8") + "&var2=" + ("test var2", "utf-8");
HttpHeaders headers = new HttpHeaders();
(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity entity = new HttpEntity(bodyValTemplate, headers);
(url, , entity, );
另外此处 HttpEntity entity
构造过程替换成下面方式也是等效的
LinkedMultiValueMap body=new LinkedMultiValueMap();
("var1","测试数据1");
("var2","test Val2");
HttpEntity entity = new HttpEntity(body, headers);
传输文件 请求
@PostMapping("/file2")
public ResponseEntity<?> fileUpload2(@RequestParam("files") ArrayList<MultipartFile> filesList) throws IOException {
MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
for (MultipartFile multipartFile : filesList) {
Resource resource = new InputStreamResource(()) {
@Override
public String getFilename() {
return ();
}
@Override
public long contentLength() {
return ();
}
};
("files", resource);
}
HttpHeaders httpHeaders = new HttpHeaders();
(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE);
HttpEntity<MultiValueMap<String, ?>> requestEntity = new HttpEntity<>(requestBody, httpHeaders);
ResponseEntity<String> responseEntity = ("http://xxxxxx/file", , requestEntity, );
return success(());
}
这是一个 Controller
层通过 RestTemplate
转发文件的示例,至于这段代码是怎么来的,请参见 官方文档 Multipart 的描述 和底部参考资料
简而言之,RestTemplate
原生通过 FormHttpMessageConverter
来支持 multipart/form-data
请求,这个过程中它认 Resource
对象作为请求的内容
FileSystemResource
符合条件,但需要一个真实文件存在于本地或远端,理想状态最好就在内存中存在,执行结束就丢弃,不在本地产生其他负作用,求其次就使用了 InputStreamResource
对象
接着追踪 FormHttpMessageConverter
代码你会发现,它依赖了 Resource
的 getFilename()
、contentLength()
方法,前者如果为 null
会忽略该文件的传输;后者则是因为默认实现是会去读流,而流只能读一次,这一次机会在计算文件长度上了就得不偿失
异步操作
AsyncRestTemplate
是用来进行异步网络请求操作的,但是在后续版本官方放弃了这个方案,推荐你使用 WebClient
,这条路子可以说是寄了(4 年前我还天真的以为自己会来补那部分内容,毕竟当时 AsyncRestTemplate
没有被标记 Deprecated)
参考资料
通过RestTemplate上传文件(InputStreamResource详解)
HttpClient与CloseableHttpClient