RestTemplate最详解
在项目中,当我们需要远程调用一个HTTP接口时,我们经常会用到RestTemplate这个类。这个类是Spring框架提供的一个工具类。Spring官网对它的介绍如下:
RestTemplate: The original Spring REST client with a synchronous, template method API.
从上面的介绍中我们可以知道:RestTemplate是一个同步的Rest API客户端。下面我们就来介绍下RestTemplate的常用功能。
1. RestTemplate简单使用
RestTemplate提供高度封装的接口,可以让我们非常方便地进行Rest API调用。常见的方法如下:
表格:RestTemplate的方法

上面的方法我们大致可以分为三组:
- getForObject --- optionsForAllow分为一组,这类方法是常规的Rest API(GET、POST、DELETE等)方法调用;
- exchange:接收一个
RequestEntity参数,可以自己设置HTTP method, URL, headers和body。返回ResponseEntity。 - execute:通过callback 接口,可以对请求和返回做更加全面的自定义控制。
一般情况下,我们使用第一组和第二组方法就够了。
1.1 创建RestTemplate
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
return restTemplate;
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(5000);
factory.setConnectTimeout(15000);
//设置代理
//factory.setProxy(null);
return factory;
}
创建RestTemplate时需要一个ClientHttpRequestFactory,通过这个请求工厂,我们可以统一设置请求的超时时间,设置代理以及一些其他细节。通过上面代码配置后,我们直接在代码中注入RestTemplate就可以使用了。
1.2 接口调用
1. 普通接口调用
Map<String, String> vars = Collections.singletonMap("hotel", "42");
//通过GET方式调用,返回一个String值,还可以给URL变量设置值(也可通过uriTemplateHandler这个属性自定义)
String result = restTemplate.getForObject(
"https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
String url = "http://127.0.0.1:8080/hello";
JSONObject param = new JSONObject();
//restTemplate会根据params的具体类型,调用合适的HttpMessageConvert将请求参数写到请求体body中,并在请求头中添加合适的content-type;
//也会根据responseType的类型(本列子中是JSONObject),设置head中的accept字段,当响应返回的时候再调用合适的HttpMessageConvert进行响应转换
ResponseEntity<JSONObject> responseEntity=restTemplate.postForEntity(url,params,JSONObject.class);
int statusCodeValue = responseEntity.getStatusCodeValue();
HttpHeaders headers = responseEntity.getHeaders();
JSONObject body = responseEntity.getBody();
2. 添加Header和Cookie
有时候,我们需要在请求中的Head中添加值或者将某些值通过cookie传给服务端,那么上面这种调用形式就不太满足要求了。
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("127.0.0.1:8080").
path("/test").build(true);
URI uri = uriComponents.toUri();
RequestEntity<JSONObject> requestEntity = RequestEntity.post(uri).
//添加cookie(这边有个问题,假如我们要设置cookie的生命周期,作用域等参数我们要怎么操作)
header(HttpHeaders.COOKIE,"key1=value1").
//添加header
header(("MyRequestHeader", "MyValue")
accept(MediaType.APPLICATION_JSON).
contentType(MediaType.APPLICATION_JSON).
body(requestParam);
ResponseEntity<JSONObject> responseEntity = restTemplate.exchange(requestEntity,JSONObject.class);
//响应结果
JSONObject responseEntityBody = responseEntity.getBody();
3. 文件上传
上面两个列子基本能覆盖我们平时开发的大多数功能了。这边再讲个文件上传的列子(RestTemplate功能还是蛮全的)。
public Object uplaod(@RequestBody JSONObject params) throws Exception{
final String url = "http://localhost:8888/hello/m3";
//设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
//设置请求体,注意是LinkedMultiValueMap
FileSystemResource resource1 = new FileSystemResource("D:\\dir1\\ss\\pic1.jpg");
FileSystemResource resource2 = new FileSystemResource("D:\\dir1\\ss\\pic2.jpg");
MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
form.add("file", resource1);
form.add("file", resource2);
form.add("param1","value1");
HttpEntity<MultiValueMap<String, Object>> files = new HttpEntity<>(form, headers);
JSONObject s = restTemplate.postForObject(url, files, JSONObject.class);
return s;
}
上面的代码中上传了两个本地图片,通过下面代码可以顺利接收。
@RequestMapping("/m3")
public Object fileUpload(@RequestParam("file") MultipartFile[] files, HttpServletRequest request) throws Exception {
//携带的其他参数可以使用getParameter方法接收
String param1 = request.getParameter("param1");
Response response = new Response();
if (files == null) {
response.failure("文件上传错误,服务端未拿到上传的文件!");
return response;
}
for (MultipartFile file : files) {
if (!file.isEmpty() && file.getSize() > 0) {
String fileName = file.getOriginalFilename();
//参考FileCopyUtils这个工具类
file.transferTo(new File("D:\\" + fileName));
logger.info("文件:{} 上传成功...",fileName);
}
}
response.success("文件上传成功");
return response;
}
但是我们发现上面的上传代码中,上传文件的类必须使用FileSystemResource。有时我们会碰到这种情况:文件我们会从文件服务下载到内存中一个InputStream的形式存在,那此时在使用FileSystemResource就不行了。
当然,我们使用讨巧一点的办法也是可以的:先将下载下来的InputStream保存到本地,然后再读取到FileSystemResource,上传后再删除本地临时文件。
但是总觉得这个方法不够完美。最后发现有个同事已经写了相关的实现。这边就直接拿来用了。
//自己实现了一个Resource
public class InMemoryResource extends ByteArrayResource {
private final String filename;
private final long lastModified;
public InMemoryResource(String filename, String description, byte[] content, long lastModified) {
super(content, description);
this.lastModified = lastModified;
this.filename = filename;
}
@Override
public long lastModified() throws IOException {
return this.lastModified;
}
@Override
public String getFilename() {
return this.filename;
}
}
调整后的上传代码
@PostMapping("/m3")
public Object m3(@RequestBody JSONObject params) throws Exception{
final String url = "http://localhost:8888/hello/m3";
//设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
//设置请求体,注意是LinkedMultiValueMap
//下面两个流从文件服务下载,这边省略(注意最后关闭流)
InputStream fis1 =
InputStream fis2 =
InMemoryResource resource1 = new InMemoryResource("file1.jpg","description1", FileCopyUtils.copyToByteArray(fis1), System.currentTimeMillis());
InMemoryResource resource2 = new InMemoryResource("file2.jpg","description2", FileCopyUtils.copyToByteArray(fis2), System.currentTimeMillis());
MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
form.add("file", resource1);
form.add("file", resource2);
form.add("param1","value1");
HttpEntity<MultiValueMap<String, Object>> files = new HttpEntity<>(form, headers);
JSONObject s = restTemplate.postForObject(url, files, JSONObject.class);
return s;
}
2. 一些其他设置
1. 拦截器配置
RestTemplate也可以设置拦截器做一些统一处理。这个功能感觉和Spring MVC的拦截器类似。配置也很简单:
class MyInterceptor implements ClientHttpRequestInterceptor{
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
logger.info("enter interceptor...");
return execution.execute(request,body);
}
}
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
MyInterceptor myInterceptor = new MyInterceptor();
List<ClientHttpRequestInterceptor> list = new ArrayList<>();
list.add(myInterceptor);
restTemplate.setInterceptors(list);
return restTemplate;
}
2. ErrorHandler配置
ErrorHandler用来对调用错误对统一处理。
public class MyResponseErrorHandler extends DefaultResponseErrorHandler {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return super.hasError(response);
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
if (statusCode == null) {
throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), response.getStatusText(),
response.getHeaders(), getResponseBody(response), getCharset(response));
}
handleError(response, statusCode);
}
@Override
protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
switch (statusCode.series()) {
case CLIENT_ERROR:
HttpClientErrorException exp1 = new HttpClientErrorException(statusCode, response.getStatusText(), response.getHeaders(), getResponseBody(response), getCharset(response));
logger.error("客户端调用异常",exp1);
throw exp1;
case SERVER_ERROR:
HttpServerErrorException exp2 = new HttpServerErrorException(statusCode, response.getStatusText(),
response.getHeaders(), getResponseBody(response), getCharset(response));
logger.error("服务端调用异常",exp2);
throw exp2;
default:
UnknownHttpStatusCodeException exp3 = new UnknownHttpStatusCodeException(statusCode.value(), response.getStatusText(),
response.getHeaders(), getResponseBody(response), getCharset(response));
logger.error("网络调用未知异常");
throw exp3;
}
}
}
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
MyResponseErrorHandler errorHandler = new MyResponseErrorHandler();
restTemplate.setErrorHandler(errorHandler);
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
//通过下面代码可以添加新的HttpMessageConverter
//messageConverters.add(new );
return restTemplate;
}
3. 简单总结
通过RestTemplate,我们可以非常方便的进行Rest API调用。但是在Spring 5中已经不再建议使用RestTemplate,而是建议使用WebClient。WebClient是一个支持异步调用的Client。所以喜欢研究新东西的同学可以开始研究下新东西了。
RestTemplate最详解的更多相关文章
- spring RestTemplate用法详解
spring RestTemplate用法详解 spring 3.2.3 框架参考有说明 21.9 Accessing RESTful services on the Client
- (转载)spring RestTemplate用法详解
前面介绍过spring的MVC结合不同的view显示不同的数据,如:结合json的view显示json.结合xml的view显示xml文档.那么这些数据除了在WebBrowser中用JavaScrip ...
- RestTemplate使用详解
1.RestTemplate添加RequestHeader如content-type可通过httpclient设置 List<Header> headers = new ArrayList ...
- Spring框架spring-web模块中的RestTemplate类详解
RestTemplate类是spring-web模块中进行HTTP访问的REST客户端核心类.RestTemplate请求使用阻塞式IO,适合低并发的应用场景. 1. RestTemplate类提供了 ...
- Spring RestTemplate详解
Spring RestTemplate详解 1.什么是REST? REST(RepresentationalState Transfer)是Roy Fielding 提出的一个描述互联系统架构风格 ...
- 【Java_Spring】RestTemplate发HTTP请求详解
Springboot — 用更优雅的方式发HTTP请求(RestTemplate详解) Spring RestTemplate提交时设置http header请求头 Spring之RestTempla ...
- 精讲RestTemplate第4篇-POST请求方法使用详解
本文是精讲RestTemplate第4篇,前篇的blog访问地址如下: 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用 精讲RestTemplate第2篇-多种底层H ...
- 精讲RestTemplate第4篇-DELETE、PUT等请求方法使用详解
本文是精讲RestTemplate第5篇,前篇的blog访问地址如下: 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用 精讲RestTemplate第2篇-多种底层H ...
- Spring MVC测试框架详解——服务端测试
随着RESTful Web Service的流行,测试对外的Service是否满足期望也变的必要的.从Spring 3.2开始Spring了Spring Web测试框架,如果版本低于3.2,请使用sp ...
随机推荐
- 个人永久性免费-Excel催化剂功能第99波-手机号码归属地批量查询
高潮过往趋于平静,送上简单的手机号码归属地查询,因接口有数量限制,仅能满足少量数据需求,如有大规模数据却又想免费获得,这就成为无解了,数据有价,且用且珍惜. 业务使用场景 除了日常自带的手机各种管家为 ...
- MapReduce之提交job源码分析 FileInputFormat源码解析
MapReduce之提交job源码分析 job 提交流程源码详解 //runner 类中提交job waitForCompletion() submit(); // 1 建立连接 connect(); ...
- .net持续集成sonarqube篇之sonarqube基本操作(一)
系列目录 Sonarqube Web管理界面虽然设计的简洁大方,也非常直观,但是由于功能繁多,这对快速入手以及快速定位到想要的功能都是一个挑战,在以后的几个小节里我们简要介绍. OwerView(概览 ...
- SQLServer 问题(一)
出现这种错误: [DBNETLIB][ConnectionOpen(Connect()).]SQL Server 不存在或拒绝访问 数据库错误 原因: 1.查看是不是没有在数据库中添加数据库服务器地址 ...
- Redis 学习笔记(篇六):数据库
Redis 是一个使用 C 语言编写的 NoSql 的数据库,本篇就讲解在 Redis 中数据库是如何存储的?以及和数据库有关的一些操作. Redis 中的所有数据库都保存在 redis.h/redi ...
- visionpro和halcon这两款机器视觉软件区别
很多朋友会问到visionpro和halcon这两款机器视觉软件,到底学哪个好呢,今天重码网就给大家讲一讲: 首先比较下两者的优缺点: halcon: 提供的图像算法要比Visionpro多,也就是说 ...
- wangEditor富文本编辑器使用及图片上传
引入js文件 <script type="text/javascript" src="style/js/wangEditor.min.js">< ...
- 【Python3爬虫】当爬虫碰到表单提交,有点意思
一.写在前面 我写爬虫已经写了一段时间了,对于那些使用GET请求或者POST请求的网页,爬取的时候都还算得心应手.不过最近遇到了一个有趣的网站,虽然爬取的难度不大,不过因为表单提交的存在,所以一开始还 ...
- 【iOS】The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods install
从 github 下载的项目经常会遇到这个问题, 如图所示: 参考: iOS 'The sandbox is not sync with the Podfile.lock'问题解决 尚未解决…………
- .net持续集成单元测试篇之单元测试简介以及在visual studio中配置Nunit使用环境
系列目录 单元测试及测试驱动开发简介 什么是单元测试 单元测试是一段自动化的代码,这段代码调用被测试的工作单元,之后对这个单元的单个最终结果的某些假设进行检验.单元测试几乎都是用单元测试框架编写的.单 ...