javaweb--Rest访问(RestTemplate)
Rest访问(RestTemplate)
在实际的项目中,往往需要发送一个Get/Post请求到其他的系统(Rest API),比如向人员管理部门请求,然后解析返回信息获取该用户的基本信息等。JDK传统的HttpURLConnection、Apache HttpClient、Netty 4和OkHttp等可以实现访问请求。不过spring的RestTemplate封装了这些操作库,使之更容易使用。
一. Rest的具体使用
1.1 get方式
RestTemplate get方式中有两个方法: getForObject和getForEntity
getForObject可以指定返回类型,getForEntity同一返回ResponseEntity
1.1.1 getForObject
有三种重载方式
(1) T getForObject(URI url, Class responseType)
(2) T getForObject(String url, Class responseType, MapString< String, ?> urlVariables)
(3) T getForObject(String url, Class responseType, Object… urlVariables)
其中url为请求url,可用通配符表示请求参数,responseType为请求返回的对象类,自动封装成对象该对象形式(如String.class 或User.class), Map< String, ?> urlVariables表示请求参数,与通配符对应即可, Object… urlVariables为请求的参数数组形式,按顺序一一匹配url中内.
下面举一个例子:
定义一个返回类型:User
public class User
{
private String name;
private Integer age;
} 省略get和set方法
发送方
private RestTemplate restTemplate = new RestTemplate();
private String name = "xiaoming";
private Integer age = 18; #重载方式3
String url = "http://localhost:8080/getUser?name={name}&age={age}";
Object[] arr = new Object[]{name, age};
User u = restTemplate.getForObject(url, User.class, arr); #重载方式2
String url = "http://localhost:8080/getUser?name={name}&age={age}";
Map<String, Object> map = new HashMap<>();
map.put("name", name);
map.put("age", age);
User u = restTemplate.getForObject(url, User.class, map); #重载方式1
#没有参数的get方式直接调用重载方式1。如
String url = "http://localhost:8080/findAllUser";
User u = restTemplate.getForObject(url, User.class); #实际生产中常用的是
String url = "http://localhost:8080/getUser?name=${name}&age=${age}";
url.replace("${name}", name)
.replace("${age}", age);
User u = restTemplate.getForObject(url, User.class);
接受方controller
public User getUser(@RequestParam String name, @RequestParam Integer age)
1.1.2 getForEntity
getForEntity与getForObject请求参数基本一样,只是返回内容不一样 .getForEntity返回ResponseEntity,里面包含返回消息内容和http headers,http 状态码。
在实际的生产环境中get请求几乎全部getForEntity,因为需要判断状态码判断接口是否调用成功(status == 10000)。返回类型往往是Json格式的字符串,然后通过转换为Json获取对应的信息。
发送方
String url = "http://localhost:8080/getUser?name={name}&age={age}";
Map<String, Object> map = new HashMap<>();
map.put("name", name);
map.put("age", age);
ResponseEntity<User> res = restTemplate.getForEntity(url, User.class, map);
User u = res.getBody();
HttpHeaders headers = res.getHeaders();
HttpStatus status = res.getStatusCode();
1.2 Post方式
post方法主要有3种方法:postForObject , postForEntity和postForLocation
1.2.1 postForObject
(1) T postForObject(URI url, Object request, Class responseType )
(2) T postForObject(String url, Object request, Class responseType, MapString< String, ?> urlVariables)
(3) T postForObject(String url, Object request, Class responseType, Object… urlVariables)
形式和getForObject类似。
发送方
String url = "http://localhost:8080/getUser";
LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("name", name);
map.add("age", age);
User u = restTemplate.postForObject(url, map, User.class);
更常见的为:
String url = "http://localhost:8080/getUser";
LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("name", name);
map.add("age", age);
HttpEntity<LinkedMultiValueMap<String, Object>> httpEntity = new HttpEntity<>(map); ##还可以设置http头
HttpHeaders headers = new HttpHeaders();
headers.add("msg", "head msg test");
HttpEntity<LinkedMultiValueMap<String, Object>> httpEntity = new HttpEntity<>(map, headers); User u = restTemplate.postForObject(url, httpEntity, User.class);
接受方
@RequestMapping(value = "/getUser", method = RequestMethod.POST)
public User get1(@RequestParam String name, @RequestParam Integer age, @RequestHeader(required = false) String msg)
1.2.2 postForEntity
postForEntity返回ResponseEntity,里面包含返回消息内容和http headers,http 状态码。在实际中,也是往往使用PostForEntity.
1.2.3 postForLocation
postForLocation返回URI,返回的是response header中的location信息,一般用于资源定位。
1.3 其他方式
http请求共有8种方式,Post和Get是最常见的两类方式。
方法 | 描述 |
GET | 请求指定的页面信息,并返回实体主体。 |
HEAD | 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头 |
POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。 |
PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
DELETE | 请求服务器删除指定的页面。 |
CONNECT | HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。 |
OPTIONS | 允许客户端查看服务器的性能。 |
TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
RestTemplate支持以下6种主要的方式。
另外,exchange和execute方法提供了以上方法的通用版本,用来支持额外的、不常用的组合(如:HTTP PATCH,带有消息体的HTTP PUT,等等)。注意,无论怎样使用底层HTTP库,都必须支持必要的组合。
1.4 配置
1.4.1 设置超时时间
spring boot 中
@Configuration
public class AppConfig
{
@Bean
@ConfigurationProperties(prefix = "custom.rest.connection")
public HttpComponentsClientHttpRequestFactory customHttpRequestFactory()
{
return new HttpComponentsClientHttpRequestFactory();
} @Bean
public RestTemplate customRestTemplate()
{
return new RestTemplate(customHttpRequestFactory());
}
}
然后在配置文件指定
custom.rest.connection.connection-request-timeout=3000
custom.rest.connection.connect-timeout=3000
custom.rest.connection.read-timeout=3000
1.4.2 上传文件
public void testUpload() throws Exception {
String url = "http://127.0.0.1:8080/test/upload.do";
String filePath = "C:\\Users\\MikanMu\\Desktop\\test.txt"; RestTemplate rest = new RestTemplate();
FileSystemResource resource = new FileSystemResource(new File(filePath));
MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
param.add("jarFile", resource);
param.add("fileName", "test.txt");
//设置头为上传
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> httpEntity = new HttpEntity<>(param, headers); String string = rest.postForObject(url, httpEntity, String.class);
}
1.4.3 下载文件
// 小文件
RequestEntity requestEntity = RequestEntity.get(uri).build();
ResponseEntity<byte[]> responseEntity = restTemplate.exchange(requestEntity, byte[].class);
byte[] downloadContent = responseEntity.getBody(); // 大文件
ResponseExtractor<ResponseEntity<File>> responseExtractor = new ResponseExtractor<ResponseEntity<File>>() {
@Override
public ResponseEntity<File> extractData(ClientHttpResponse response) throws IOException {
File rcvFile = File.createTempFile("rcvFile", "zip");
FileCopyUtils.copy(response.getBody(), new FileOutputStream(rcvFile));
return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders()).body(rcvFile);
}
};
File getFile = this.restTemplate.execute(targetUri, HttpMethod.GET, null, responseExtractor);
二. RestTemplate的原理(转)
RestTemplate包含以下几个部分:
- HttpMessageConverter 对象转换器
- ClientHttpRequestFactory 默认是JDK的HttpURLConnection
- ResponseErrorHandler 异常处理
- ClientHttpRequestInterceptor 请求拦截器
2.1 RestTemplate类图
2.2 postForEntity 处理过程
以postForEntity方法作为切入点,来梳理一下请求是如何执行的,以下概要流程图。(灰色方框内为doExcute方法的内部处理。)
postForEntity方法中,创建了两个内部类对象requestCallback和responseExtractor并传递给execute方法,分别用于请求和响应的关键处理。
总结了一下,不管是请求还是响应,这里的关键处理就是明确资源的媒体类型(也就是要明确请求端和响应端交换的信息的格式),
根据媒体类型选择适合的解析器,将消息写入输出流或者从输入流读入。
2.3 requestCallback.doWithRequest 处理过程
——内部类AcceptHeaderRequestCallback.doWithRequest的处理。
发送请求时,Http头部需要设置Accept字段,该字段表明了发送请求的这方接受的媒体类型(消息格式),也是响应端要返回的信息的媒体类型(消息格式)。
根据postForEntity方法的第三个参数responseType,程序将选择适合的解析器XXXConverter,并依据该解析器找出所有支持的媒体类型。
——内部类HttpEntityRequestCallback.doWithRequest的处理。
如果是POST请求并且消息体存在时,除了设置Accept字段,还可能需要设置Content-Type字段,该字段表明了所发送请求的媒体类型(消息格式),也是响应端接受的媒体类型(消息格式)。
根据postForEntity方法的第二个参数request,程序将选择适合的解析器XXXConverter,将请求消息写入输出流。
2.4 responseExtractor.extractData 处理过程
与请求消息体的处理过程相似。
虽然,postForEntity方法中responseExtractor对象的类型为ResponseEntityResponseExtractor,但是实际执行处理过程是HttpMessageConverterExtractor的对象实例。
在postForObject方法中,则是直接使用了HttpMessageConverterExtractor创建对象。 下图画出的也是HttpMessageConverterExtractor类中的extractData方法的处理过程。
2.5 关于GenericHttpMessageConverter
在以上几个方法的梳理过程中,我注意到每次消息解析转换都要作GenericHttpMessageConverter分支判断,为什么呢?
GenericHttpMessageConverter接口继承自HttpMessageConverter接口,二者都是在org.springframework.http.converter路径下。
此包中还有其他几种Converter实现类,看名字就可以猜到主要功能。唯独GenericHttpMessageConverter没猜出来。
于是,我在eclipse中使用Ctrl+Shift+G快捷键搜索了一下它的实现类AbstractGenericHttpMessageConverter。
看到AbstractJackson2HttpMessageConverter类的时候,我好像明白了。
GenericHttpMessageConverter是其他转换器派生类的接口,用于解析特殊格式的资源,比如json,xml等。
2.6 关于RestTemplate 中的转换器列表
转换器列表messageConverters是final类型的,由RestTemplate的构造函数赋值。一旦创建了RestTemplate对象,该对象也就同时拥有了一个当前系统支持的转换器列表。
那么,对于需要引用jar包的转换器,RestTemplate是怎么添加转换器实例的呢?
在声明messageConverters列表之前,定义了几个布尔型静态常量,该常量是对某一个特殊类是否可以被加载的判断结果。
在RestTemplate的构造函数中,根据该常量值来判断是否将某个转换器的实例加入到列表中。
private static boolean romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", RestTemplate.class.getClassLoader()); private static final boolean jaxb2Present =
ClassUtils.isPresent("javax.xml.bind.Binder", RestTemplate.class.getClassLoader()); private static final boolean jackson2Present =
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", RestTemplate.class.getClassLoader()) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", RestTemplate.class.getClassLoader()); private static final boolean jackson2XmlPresent =
ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", RestTemplate.class.getClassLoader()); private static final boolean gsonPresent =
ClassUtils.isPresent("com.google.gson.Gson", RestTemplate.class.getClassLoader()); private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
由此可知,RestTemplate的初始化顺序:
创建(new)一个RestTemplate实例时,首先装载RestTemplate类,然后按照出现的顺序转载静态变量或代码。
装载完成之后,进行实例化。首先实例化成员变量,然后执行构造函数。
那么,外部引用的类是否可以被加载具体是怎么判断的?
通过ClassUtils.isPresent(String className, ClassLoader classLoader)方法。——感觉ClassUtils可以单独写一篇orz
ClassUtils 类在 spring-core 工程的 org.springframework.util 路径下。
简单来说,isPresent实际上是返回了ClassUtils.forName方法的处理结果,当forName方法正常执行,则鉴定的类被加载,返回true;若抛出异常(注意,此处异常是Throwable)则返回false。
forName方法的处理是:
首先,根据类名的长度(<=8)来确定是否是原始类型,若是原始类型则返回类对象Class
javaweb--Rest访问(RestTemplate)的更多相关文章
- REST访问(RestTemplate)
https://www.cnblogs.com/softidea/p/6910198.html 经常需要发送一个GET/POST请求到其他系统(REST API),通过JDK自带的HttpURLCon ...
- IDEA 修改JavaWeb的访问路径
问题描述 对于我这个刚刚使用IDEA不久的新手来说,能够正常运行就不错了,不过到了后面,可能会觉得IDEA给你分配的默认访问路径很不顺手,比如访问的时候需要通过: http://loca ...
- Java-Web中访问某个指定工程中的文件,报错后发现访问的文件是另一个工程里面的文件
问题: 浏览器向我的bingou项目中的UserDaoImpl.java发送请求, myeclipse报错:空指针异常 点击报错行之后,错误给定位到了另一个项目中的的一个文件 解决: 原因是文件名错误 ...
- JavaWeb学习总结(六)—HttpServletResponse
Response概述: response是Servlet.service方法的一个参数,类型为javax.servlet.http.HttpServletResponse.在客户端发出每个请求时,服务 ...
- Javaweb项目 利用JSP响应浏览器
一.javaweb 数据访问流程? 1.浏览器 http 访问服务器 找到 servlet(HttpServeltDemo.java文件) 2.servle 通过dao 访问数据库 数据库将数据返回 ...
- velocity-1.7学习笔记
Velocity是由Apache软件组织提供的一项开放源码项目,它是一个基于Java的模板引擎.通过Velocity模板语言(Velocity Template Language,VTL)定义模板(T ...
- myeclipse笔记(3):导入的项目切换jdk版本
有时候,从外面导入的javaweb项目会访问不了,这个时候改变jdk版本就是其中解决的方法之一. 右键点击项目 --> bulid path --> configure 选择需要 ...
- Web初学-Web应用细节
一.web应用程序简介 WEB应用程序指供浏览器访问的程序,通常也简称为web应用. 一个web应用由多个静态web资源和动态web资源组成,如: html.css.js文件 Jsp文件.java程序 ...
- Servlet第五篇【介绍会话技术、Cookie的API、详解、应用】
什么是会话技术 基本概念: 指用户开一个浏览器,访问一个网站,只要不关闭该浏览器,不管该用户点击多少个超链接,访问多少资源,直到用户关闭浏览器,整个这个过程我们称为一次会话. 为什么我们要使用会话技术 ...
- ServletConfig、ServletContext 的应用
一.ServletConfig对象及其应用(用的不多) 1. Context和ContextPath:一个web工程,若名为JavaWeb,访问的路径为:http://localhost:8080/J ...
随机推荐
- MySQL之Prepared Statements
1.概述 prepared statement在MySQL4.1中引进并且增加了一些新的命令: COM_STMT_PREPARE COM_STMT_EXECUTE COM_STMT_CLOSE COM ...
- log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment)的解决
报错:log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironme ...
- win下如何生成 github ssh公钥 GIT
1. 安装git,可以去官网下最新的,但由于局域网问题,我们百度git即可,一般版本也比较新: 2. 一路默认下一步,安装成功后,从程序目录打开 "Git Bash": 3. 输入 ...
- k8s集群之上运行etcd集群
一.知识点: 1.headless services NOTE:: 我们在k8s上运行etcd集群,集群间通告身份是使用dns,不能使用pod ip,因为如果pod被重构了ip会变,在这种场景中不能直 ...
- MySQL的My.cnf模板(转)
[client] default-character-set = utf8mb4 port = PORT socket = /srv/myPORT/run/mysql.sock [mysqld] us ...
- FastJSON JSONObject 字段排序 Feature.OrderedField
package cn.tongdun.robot.web; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeRefe ...
- 如何利用AI识别未知——加入未知类(不太靠谱),检测待识别数据和已知样本数据的匹配程度(例如使用CNN降维,再用knn类似距离来实现),将问题转化为特征搜索问题而非决策问题,使用HTM算法(记忆+模式匹配预测就是智能),GAN异常检测,RBF
https://www.researchgate.net/post/How_to_determine_unknown_class_using_neural_network 里面有讨论,说是用rbf神经 ...
- Spring4 -03 -Dependency Injection (依赖注入) : 代码体现/配置xml/测试
DI:中文名称:依赖注入 英文名称((Dependency Injection) DI 是什么? 3.1 DI 和IoC 是一样的,差不多一样的技术和模板! 3.2 当一个类(A)中需要依赖另一个类( ...
- PHP——curl设置请求头需要注意哪些
前言 在设置这个请求头上踩了一些坑,此文记录下. 步骤 设置请求头 curl_setopt($ch, CURLOPT_HTTPHEADER, $header); 请求头写法 一定不要忘记:不然进行请求 ...
- Ubuntu常见服务启停
停止rpcbind服务service portmap stop 停止nfs服务service nfs-kernel-server stop 停止Telnet服务/etc/xinetd.d/telnet ...