RestTemplate 详解

在项目中,当我们需要远程调用一个 HTTP 接口时,我们经常会用到 RestTemplate 这个类。这个类是 Spring 框架提供的一个工具类。Spring 官网对它的介绍如下:

RestTemplate: The original Spring REST client with a synchronous, template method API.

从上面的介绍中我们可以知道:RestTemplate 是一个同步的 Rest API 客户端。下面我们就来介绍下 RestTemplate 的常用功能。

 

RestTemplate 简单使用#

RestTemplate 提供高度封装的接口,可以让我们非常方便地进行 Rest API 调用。常见的方法如下:

表格:RestTemplate 的方法

上面的方法我们大致可以分为三组:

  • getForObject --- optionsForAllow 分为一组,这类方法是常规的 Rest API(GET、POST、DELETE 等)方法调用;
  • exchange:接收一个 RequestEntity 参数,可以自己设置 HTTP method,URL,headers 和 body,返回 ResponseEntity;
  • execute:通过 callback 接口,可以对请求和返回做更加全面的自定义控制。

一般情况下,我们使用第一组和第二组方法就够了。

创建 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 就可以使用了。

有时候我们还需要通过 ClientHttpRequestFactory 配置最大链接数,忽略SSL证书等,大家需要的时候可以自己查看代码设置。

 

接口调用#

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").// 添加 headerheader(("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);// 设置请求体,注意是 LinkedMultiValueMapFileSystemResource 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;}@Overridepublic long lastModified() throws IOException {return this.lastModified;}@Overridepublic 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;}

4. 文件下载

private InputStream downLoadVideoFromVod(String url) throws Exception {byte[] bytes = restTemplate.getForObject(url, byte[].class);return new ByteArrayInputStream(bytes);
}

 

一些其他设置#

1. 拦截器配置

RestTemplate 也可以设置拦截器做一些统一处理。这个功能感觉和 Spring MVC 的拦截器类似。配置也很简单:

class MyInterceptor implements ClientHttpRequestInterceptor{@Overridepublic 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 {@Overridepublic boolean hasError(ClientHttpResponse response) throws IOException {return super.hasError(response);}@Overridepublic 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);}@Overrideprotected 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. HttpMessageConverter 配置
RestTemplate 也可以配置 HttpMessageConverter,配置的原理和 Spring MVC 中类似。

 

简单总结#

通过 RestTemplate,我们可以非常方便的进行 Rest API 调用。但是在 Spring 5 中已经不再建议使用 RestTemplate,而是建议使用 WebClient。WebClient 是一个支持异步调用的 Client。所以喜欢研究新东西的同学可以开始研究下新东西了。

 

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

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

相关文章

初识Spark2.0之Spark SQL

内存计算平台Spark在今年6月份的时候正式发布了spark2.0&#xff0c;相比上一版本的spark1.6版本&#xff0c;在内存优化&#xff0c;数据组织&#xff0c;流计算等方面都做出了较大的改变&#xff0c;同时更加注重基于DataFrame数据组织的MLlib&#xff0c;更加注重机器学习整…

webpack开发Vue配置

一直以来使用webpack都是用的别人的配置&#xff0c;这几天自己学习了一下。 项目地址&#xff1a;https://github.com/donghaohao... 新建整个工程 npm init安装依赖&#xff0c;这里我们开发vue项目&#xff0c;npm install vue --save&#xff0c;然后是开发时的依赖npm ins…

ABP详细教程——模块类

概述模块化是ABP vNext的最大亮点&#xff0c;也是ABP vNext框架的核心&#xff0c;而模块类是ABP vNext框架模块化的核心要素。这一章节&#xff0c;我就从模块类的用法、运行机制、源代码等层面&#xff0c;带大家详细了解ABP vNext的模块类。用法在ABP的约定中&#xff0c;每…

[转]Eureka工作原理

目录 Eureka 工作原理 Eureka 核心概念 自我保护机制 Eureka 集群原理 Eurka 工作流程 总结 Eureka 工作原理 上节内容为大家介绍了&#xff0c;注册中心 Eureka 产品的使用&#xff0c;以及如何利用 Eureka 搭建单台和集群的注册中心。这节课我们来继续学习 Eureka&…

centos7下别名(alias)的特殊用法

版权声明&#xff1a;转载请注明出处:http://blog.csdn.net/dajitui2024 https://blog.csdn.net/dajitui2024/article/details/79438200 参考&#xff1a;https://www.cyberciti.biz/faq/bash-bypass-alias-command-on-linux-macos-unix/ 正常情况下&#xff0c;定义过的别名&a…

解决WDCP3环境gbk网站编码程序乱码问题

因为默认WDCP V3版本环境编码格式是UTF-8版本&#xff0c;如果我们程序采用的是GBK编码肯定都会有乱码问题。 我们到WDCP后台&#xff0c;"网站管理"-"PHP设置"&#xff0c;看到上图所示&#xff0c;准备直接在线编辑PHP.INI文件。 这里我们找到"defa…

重谈联想5G编码投票事件

此前&#xff0c;司马南谈了联想好几个问题&#xff0c;其中最尖锐的要属国有资产流失&#xff0c;这是联想管理层无法回避的死穴。不过&#xff0c;司马南批判联想5G投票背刺H公司&#xff0c;这基本就是造谣了。当年&#xff0c;媒体把编码投票炒作的很厉害&#xff0c;抨击联…

JStorm2.1.1集群的安装和使用

为什么80%的码农都做不了架构师&#xff1f;>>> JStorm2.1.1集群的安装和使用 Storm是一个免费开源、分布式、高容错的实时计算系统&#xff0c;而JStorm是阿里巴巴开源的基于Storm采用Java重写的一套分布式实时流计算框架&#xff0c;在性能和支持的集群规模上做了…

Hystrix 原理

Hystrix是什么&#xff1f; Hystrix是Netflix开源库&#xff0c;这是一个针对分布式系统的延迟和容错库。 Hystrix 供分布式系统使用&#xff0c;提供延迟和容错功能&#xff0c;隔离远程系统、访问和第三方程序库的访问点&#xff0c;防止级联失败&#xff0c;保证复杂的分布…

「深度」无人机实名制政策特稿|市场看好、资本关注,“反黑飞”正在崛起

从政策和需求来看&#xff0c;“反黑飞”越来越重要&#xff0c;市场也正在不断崛起。 对于大多数人来说&#xff0c;今天是最适合明目张胆“装嫩”的六一儿童节。不过&#xff0c;在无人机厂商和无人机玩家的眼里&#xff0c;今天是无人机实名制政策正式实施的日子。 近年来&…

在navicat中新建数据库

前言&#xff1a; 在本地新建一个名为editor的数据库&#xff1b; 过程&#xff1a; 1.&#xff1b; 2.选择&#xff1a;utf8mb4 -- UTF-8 Unicode字符集&#xff0c;原因在于&#xff1a;utf8mb4兼容utf8&#xff0c;且比utf8能表示更多的字符。&#xff0c;而且它支持表情符号…

MASA Stack 第三期社区例会

MASA Blazor 0.5.0发版内容功能Autocomplete&#xff1a;支持通过设置AutoSelectFirst参数开启自动选择第一项的功能&#xff0c;支持CacheItems参数&#xff0c;增强使用上下键的用户体验。BottomNavigation&#xff1a;&#xff1a;一个替代侧边栏的新组件。它主要用于移动应…

MySQL添加用户、删除用户与授权

MySql中添加用户,新建数据库,用户授权,删除用户,修改密码(注意每行后边都跟个;表示一个命令语句结束): 1.新建用户 1.1 登录MYSQL&#xff1a; >mysql -u root -p >密码 1.2 创建用户&#xff1a; mysql> insert into mysql.user(Host,User,Password) values("lo…

[转]高并发架构设计之--「服务降级」、「服务限流」与「服务熔断」

目录 服务降级 1 、简介 2 、使用场景 3 、核心设计 3.1 分布式开关 3.2 自动降级分类 3.3 配置中心 3.4 处理策略 3.5 降级分类 3.6 服务降级要考虑的问题 4 、高级特性 4.1 分级降级 4.2 降级权值 5 、总结与展望 服务限流 一、为什么要做服务限流设计&…

【Linux】【Services】【nfs】nfs安装与配置

1. 概念 1.1. NFS&#xff1a;Network File System&#xff0c;传统意义上&#xff0c;文件系统在内核中实现。 1.2. RPC&#xff1a;Remote Procedure Call protocol&#xff0c;远程过程调用&#xff0c;函数调用&#xff08;远程主机上的函数&#xff09; 1.3. 端口&#xf…

SpringBoot获取ApplicationContext

2019独角兽企业重金招聘Python工程师标准>>> 有两种方法&#xff1a; 创建Component实现ApplicationContextAware接口&#xff0c;SpringBoot会自动调用这个类的setApplicationConext()方法。鼓励使用这种方式。SpringApplication.run(MyApplication.class, args)这…

SkiaSharp 之 WPF 自绘 投篮小游戏(案例版)

此案例主要是针对光线投影法碰撞检测功能的示例&#xff0c;顺便做成了一个小游戏&#xff0c;很简单&#xff0c;但是&#xff0c;效果却很不错。投篮小游戏规则&#xff0c;点击投篮目标点&#xff0c;就会有一个球沿着相关抛物线&#xff0c;然后&#xff0c;判断是否进入篮…

zuul集成ribbon完成服务通信和负载均衡

目录 Zuul2服务通信 超时相关 默认超时配置 自定义超时配置 负载均衡 Zuul2服务通信 描述&#xff1a;zuul2通过Ribbon完成客户端负载均衡以及与服务器群集进行通信。 zuul2的通信是集成Ribbon实现的&#xff0c;在Origin中集成Ribbon基本配置&#xff08;例如IClientCo…

时任上海来伊份互联网事业群总裁王戈钧 :传统企业(线上+线下)移动互联网改造...

2017年12月22日-23日&#xff0c;第13届信息化领袖峰会暨2017中国数字化贡献人物颁奖盛典在上海盛大开幕。本次峰会由上海市经济和信息化委员会指导&#xff0c;上海市国有资产信息中心、上海市计算机用户协会、上海市信息服务业行业协会、上海大数据联盟、上海市高等教育学会支…

Linux系统时间\硬件时间(date、tzselect、clock、hwclock、ntpdate)

1、系统时间和硬件时间 在Linux中有硬件时钟与系统时钟两种时钟。硬件时钟是指主机板上的时钟设备&#xff0c;也就是通常可在BIOS画面设定的时钟。系统时钟则是指kernel中的时钟。所有Linux相关指令与函数都是读取系统时钟的设定。因为存在两种不同的时钟&#xff0c;那么它们…