RestTemplate
简介
RestTemplate
是由Spring
框架提供的一个可用于应用中调用rest
服务的类它简化了与http
服务的通信方式,统一了RESTFul
的标准,封装了http
连接,我们只需要传入url
及其返回值类型即可。相较于之前常用的HttpClient
,RestTemplate
是一种更为优雅的调用RESTFul
服务的方式。- 在
Spring
应用程序中访问第三方REST服务
与使用Spring RestTemplate
类有关。RestTemplate
类的设计原则与许多其他Spring
的模板类(例如JdbcTemplate
)相同,为执行复杂任务提供了一种具有默认行为的简化方法。 RestTemplate
默认依赖JDK提供了http
连接的能力(HttpURLConnection
),如果有需要的话也可以通过setRequestFactory
方法替换为例如Apache HttpCompoent、Netty或OKHttp
等其他Http libaray
。- 考虑到了
RestTemplate
类是为了调用REST服务而设计的,因此它的主要方法与REST
的基础紧密相连就不足为奇了,后者时HTTP
协议的方法:HEAD、GET、POST、PUT、DELETE、OPTIONS
例如,RestTemplate
类具有headForHeaders()、getForObject()、putForObject(),put()和delete()
等方法。
创建
RestTemplate
因为
RestTemplate
是
Spirng
框架提供的所以只要是一个
Springboot
项目就不用考虑导包的问题,这些都是提供好的。
但是
Spring
并没有将其加入
SpringBean
容器中,需要我们手动加入,因为我们首先创建一个
Springboot
配置类,再在配置类中将我们的
RestTemlate
注册到
Bean
容器中
方法一
使用
Springboot
提供的
RestTemplateBuilder
构造类来构造一个
RestTemplate
,可以自定义一些连接参数,如:连接超时时间,读取超时时间,还有认证信息等
@ConfigurationpublicclassWebConfiguration{@BeanpublicRestTemplaterestTemplate(RestTemplateBuilder builder){return builder
//设置连接超时时间.setConnectTimeout(Duration.ofSeconds(5000))//设置读取超时时间.setReadTimeout(Duration.ofSeconds(5000))//设置认证信息.basicAuthentication("username","password")//设置根路径.rootUri("https://api.test.com/")//构建.build();}}
添加自定义的拦截器
自定义拦截器示例
@Slf4jpublicclassCustomClientHttpRequestInterceptorimplementsClientHttpRequestInterceptor{@OverridepublicClientHttpResponseintercept(HttpRequest request,byte[] body,ClientHttpRequestExecution execution)throwsIOException{//打印请求明细logRequestDetails(request,body);ClientHttpResponse response = execution.execute(request, body);//打印响应明细logResponseDetails(response);return response;}privatevoidlogRequestDetails(HttpRequest request,byte[] body){
log.debug("Headers:{}",request.getHeaders());
log.debug("body:{}",newString(body,StandardCharsets.UTF_8));
log.debug("{}:{}",request.getMethod(),request.getMethodValue());}privatevoidlogResponseDetails(ClientHttpResponse response)throwsIOException{
log.debug("Status code : {}",response.getStatusCode());
log.debug("Status text : {}",response.getStatusText());
log.debug("Headers : {}",response.getHeaders());
log.debug("Response body: {}",StreamUtils.copyToString(response.getBody(),StandardCharsets.UTF_8));}}
使用
RestTemplateBuilder
构造类,添加自定义拦截器,构造带有自定义拦截器的
RestTemplate
实例
@ConfigurationpublicclassWebConfiguration{@BeanpublicRestTemplaterestTemplate(RestTemplateBuilder builder){return builder
.additionalInterceptors(newCustomClientHttpRequestInterceptor())//构建.build();}}
测试请求确实经过了拦截器,注册成功(注意请求和响应的流只会被读取一次,这里我们读取了response后返回的response就读取不到刚刚读过的内容了)
方法二
使用
RestTemplate
构造方法构造一个
RestTemlate
,虽然不能像
RestTemplate
构造类那样更详细、更多样的配置参数,但是
RestTemplate
构造方法在一般情况是够用的。
- 无参构造 全部参数默认
- 指定
ClientHttpRequestFactory
的构造方法可以指定自己实现的ClientHttpRequestFactory
(客户端http
请求工厂)其他的与无参构造相同。 -ClientHttpRequestFactory
- 指定
List<HttpMessageConverter<?>>
的构造方法可以指定自己是实现的HttpMessageConverter
(Http
消息转换器)传入其他与无参构造相同。
@ConfigurationpublicclassWebConfiguration{@BeanpublicRestTemplaterestTemplate(){returnnewRestTemplate();}}
两者方法都可使用,前者提供了多样的自定义参数的选择,可以将
RestTemplate
配置的更为完善,后者则简化了配置虽然配置多样性不如前者,但是日常使用调用些
API
还是足以使用
RestTemplate API
使用
在使用
RestTemplate
前先让我们看看
RestTemplate
有哪些
API
相信大家看到这么多方法,一定很头大,但是我们仔细看上述的方法,我们可以提取出主要的几种方法是(这里只讨论Http请求的):
GET
POST
PUT
DELETE
HEAD
OPTIONS
EXCHANGE
EXECUTE
这里我给大家安利一个一个网站,它提供免费的RESTFul api的样例测试。httpbin A simple HTTP Request & Response Service.
GET
通过上图我们可以发现
RestTemlate
发送
GET
请求的方法有两种
public <T> T getForObject(...)
public <T> ResponseEntity<T> getForEntity(...)
getForEntity()
后缀带有
Entity
的方法都代表返回一个
ResponseEntity<T>
,
ResponseEntity<T>
是Spring对
HTTP
请求响应的封装,包括了几个重要的元素,如响应码,
contentType、contentLength
、响应消息体等
通过它继承父类(
HttpEntity<T>
)的
getHeader()
方法我们可以获取
contentType、contentLength
、响应消息体等。比如下面这个例子。
publicvoidqueryWeather(){ResponseEntity<Object> forEntity = restTemplate.getForEntity("https://restapi.amap.com/v3/weather/weatherInfo?city=510100&key=e7a5fa943f706602033b6b329c49fbc6",Object.class);System.out.println("状态码:"+forEntity.getStatusCode());System.out.println("状态码内容:"+forEntity.getStatusCodeValue());HttpHeaders headers = forEntity.getHeaders();System.out.println("响应头:"+headers);Object body = forEntity.getBody();System.out.println("响应内容:"+body);}
该例子中
getForEntity()
方法的第一个参数为我要调用服务的
URL
,第二个参数则为响应内容的类的类型(Java嘛 万物皆对象)还可以添加第三个参数,第三个参数为一个可变参数 代表着调用服务时的传参。
第三个参数可以使用key-value的map来传入参数
get请求也可通过向在url上添加查询参数来发送带有请求的参数
getForObject()
相比于前者
getForEntity()
该方法则是,更偏向于直接获取响应内容的,因为他直接返回响应实体的
body
(响应内容),。比如下面这个例子
publicvoidqueryWeather(){Object body = restTemplate.getForObject("https://restapi.amap.com/v3/weather/weatherInfo?city=510100&key=e7a5fa943f706602033b6b329c49fbc6",Object.class);System.out.println(body);}
方法参数签名与`getForEntity()`基本一致。
当你只需要返回的响应内容时,使用
getForObject()
是一个很好的选择,但当你需要获得更详细的响应信息,如响应头中的信息,你就只能选择
getForEntity()
了。
POST
POST
请求有如下三种方法
public URI postForLocation(...)
public <T> T postForObject(...)
public <T> ResponseEntity<T> postForEntity(...)
后两种用法与
GET
基本一致不做详细介绍,这里着重介绍
postForLocation()
postForEntity()
该方法有三个参数,第一个为调用服务的地址(URL)
第二个参数表示上传的参数(json格式提交)
第三个表示返回响应内容的具体类型
第四个参数也用于指定参数(在URL中添加)
@OverridepublicvoidqueryWeather(){User user =newUser();
user.setName("鲁大师");ResponseEntity<Object> objectResponseEntity = restTemplate.postForEntity("https://restapi.amap.com/v3/weather/weatherInfo?city=510100&key=e7a5fa943f706602033b6b329c49fbc6", user,Object.class);System.out.println("消息响应内容:"+objectResponseEntity.getBody());}
postForObject()
使用方法与
getForObject
类似只是多了一个传入对象参数(传入方式与
postForEntity()
相同)
publicvoidqueryWeather(){User user =newUser();
user.setName("鲁大师");ResponseEntity<Object> objectResponseEntity = restTemplate.postForEntity("https://httpbin.org/post", user,Object.class);MediaType contentType = objectResponseEntity.getHeaders().getContentType();System.out.println(contentType);System.out.println("消息响应内容:"+objectResponseEntity.getBody());}
postForLocation()
postForLocation
传参用法与前两者一致,只不过返回从实体变成了一个
URL
,因此它不需要指定返回响应内容的类型。
publicvoidqueryWeather(){User user =newUser();
user.setName("鲁大师");URI uri = restTemplate.postForLocation("https://httpbin.org/post", user);System.out.println(uri);}
这个只需要服务提供者返回一个 URI 即可,该
URI
返回值体现的是:用于提交完成数据之后的页面跳转,或数据提交完成之后的下一步数据操作
URI
。
使用POST以表单方式提交
这里我们着重说一下,如何自己封装一个请求体。
我们需要用到如下几个类
HttpHeaders
MultiValueMap<K,V>
HttpEntity<T>
HttpHeaders
故名思意,就是用来封装Http请求的请求头的,这里我们要设置他的
ContentType
为**
MediaType.APPLICATION_FORM_URLENCODED
**以使得我们提交的参数是以Form(表单)的形式提交。
//设置请求头, x-www-form-urlencoded格式的数据HttpHeaders httpHeaders =newHttpHeaders();//这里指定参数以UTF-8编码格式传输MediaType mediaType =newMediaType(MediaType.APPLICATION_FORM_URLENCODED, UTF_8);
httpHeaders.setContentType(mediaType);//提交参数设置MultiValueMap<String,String> map =newLinkedMultiValueMap<>();
map.add("name","鲁大师");
MultiValueMap<K,V>
该类是用来封装请求参数的,是以
key-value
的形式封装但是以单个key对应多个value的格式传输(也就是是以单个
key:[value...]
的格式传输的)。
//提交参数设置MultiValueMap<String,String> map =newLinkedMultiValueMap<>();
map.add("name","鲁大师");
**如果像传输单个
key
对应单个
value
使用普通的
Map
传参即可**
HttpEntity<T>
该类是用来封装请求的,主要作用就是将请求头和请求体封装在一起成为一个请求实体 T用来指定用来封装参数的容器的类型。
//组装请求体HttpEntity<MultiValueMap<String,String>> request =newHttpEntity<>(map, httpHeaders);
测试
通过上述介绍后,我们就可以自己封装一个以form形式提交参数的
POST
请求了。
@TestvoidcontextLoads(){//请求地址String url ="https://httpbin.org/post";//设置请求头, x-www-form-urlencoded格式的数据HttpHeaders httpHeaders =newHttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);//提交参数设置MultiValueMap<String,String> map =newLinkedMultiValueMap<>();
map.add("name","鲁大师");//组装请求体HttpEntity<MultiValueMap<String,String>> request =newHttpEntity<>(map, httpHeaders);//发送post请求并打印结果 以String类型接收响应结果JSON字符串String s = restTemplate.postForObject(url, request,String.class);System.out.println(s);}
通过拦截器拦截了请求并对请求头进行拆包,可以发现
ContentType
已经被修改成了
x-www-form-urlencoded
格式了。
PUT
PUT
请求的方法只有一类
void put()
PUT()
使用方法与
postForEntity()
参数基本一致,只是
put
方法没有返回值(也就不必去设置响应内容的类型了)。
@TestvoidcontextLoads(){//请求地址String url ="http://httpbin.org/put";User user =newUser();
user.setName("鲁大师");
restTemplate.put(url,user);}
DELETE
与
PUT
一样,
DELETE
方法只有一类
void delete()
delete()
delete()
可以指定
url
中的中的参数,但是
RestTemplate
的
delete()
方法是不支持上传
requestBody
的。
voidcontextLoads(){//请求地址String url ="http://httpbin.org/delete";
restTemplate.delete(url);}
HEADER
HEADER
也只有一类方法
public HttpHeaders headForHeaders()
主要用来发送请求获取响应头部信息,但是像
DELETE
、
PUT
这类没有响应的方法,是不能使用该方法的(因为没有响应也就没有响应头了)。
@TestvoidcontextLoads(){//请求地址String url ="http://httpbin.org/get";HttpHeaders httpHeaders = restTemplate.headForHeaders(url);System.out.println(httpHeaders);}
OPTIONS
public Set<HttpMethod> optionsForAllow()
该方法的主要用来判断该服务地址,能够使用那种方法去执行
@TestvoidcontextLoads(){//请求地址String url ="http://httpbin.org/get";Set<HttpMethod> httpMethods = restTemplate.optionsForAllow(url);System.out.println(httpMethods);}
EXCHANGE
<T> ResponseEntity<T> exchange()
该接口与其他接口不同
- 该方法允许用户指定请求的方法(
get,post,put
等)- 可以在请求中增加body以及头信息,其内容通过参数
HttpEntity<?> requestEntity
描述exchange
支持’含参数的类型(即泛型)'作为返回类型,该特性通过ParameterizedTypeReferenceresponseType
描述
该方法支持五个参数
- 第一个是服务地址
- 第二个是请求方法
- 第三个是写入的请求实体
- 第四个是响应内容的类型
- 第五个是扩展模板的变量或包含
URI
模板变量的映射
@TestvoidcontextLoads(){//请求地址String url ="http://httpbin.org/post";User user =newUser();
user.setName("彭于晏");HttpHeaders httpHeaders =newHttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);HttpEntity<User> userHttpEntity =newHttpEntity<>(user, httpHeaders);ResponseEntity<Object> exchange = restTemplate.exchange(url,HttpMethod.POST, userHttpEntity,Object.class);System.out.println(exchange);}
上述代码模拟了一个简单的
POST
请求 可以理解为可以动态的指定请求方法和请求实体的一个方法。
响应实体
EXECUTE
<T> T execute()
该方法就是执行请求的方法,我们可以发现上述的所有方法的最后执行都是调用的该方法执行,所以他在
RestTemplate
中十分重要
该方法有五个参数
- 服务地址
- 请求的方法
- 准备请求的对象(
requestCallback
)- 从响应中提取返回值的对象
- 扩展模板的变量或包含
URI
模板变量的映射
execute()
@Override@Nullablepublic<T>Texecute(String url,HttpMethod method,@NullableRequestCallback requestCallback,@NullableResponseExtractor<T> responseExtractor,Object... uriVariables)throwsRestClientException{URI expanded =getUriTemplateHandler().expand(url, uriVariables);returndoExecute(expanded, method, requestCallback, responseExtractor);}
通过上述源码我们可以发现
execute()
方法只是将我们传入的
String
类型的
URL
转换为了
URL
类型,最后执行请求是由
doExecute()
方法
doExecute()
这里需要了解两个类:
RequestCallback
和
ResPonseExtractor
RequestCallback
: 用于操作请求头和body,在请求发出前执行。不需要关心关闭请求或处理错误:这都将由RestTemplate处理。
该接口有两个实现类:
ResPonseExtractor
: 解析HTTP响应的数据,而且不需要担心异常和资源的关闭。
该接口在
RestTemplate
中同样有两个实现类:
HeadersExtractor
提取响应
HttpHeaders
的响应提取器。直接提取响应体中的响应头
ResponseEntityResponseExtractor<T>
HttpEntity
的响应提取器。可以获取响应实体里面包括响应头,响应体等。具体请查看
HttpEntity
@TestvoidcontextLoads(){//请求地址String url ="http://httpbin.org/post";User user =newUser();
user.setName("彭于晏");HttpHeaders httpHeaders =newHttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);HttpEntity<User> userHttpEntity =newHttpEntity<>(user, httpHeaders);ResponseEntity<Object> execute = restTemplate.execute(url,HttpMethod.POST, restTemplate.httpEntityCallback(userHttpEntity), restTemplate.responseEntityExtractor(Object.class));System.out.println(execute);}
解惑
- 前面我们介绍方法的时候发现有个一个可变参数,那个参数被描述成了扩展模板的变量或是包含
URI
模板变量的映射
我们来简单看一下这个参数,我们知道请求传参可以通过
url
拼接参数的方式传参,拼接参数也分为两种:
- 路径中嵌入占位的格式(
http://httpbin.org/{1}/post
)也叫模板映射- 末尾添加
Key-value
格式(http://httpbin.org/post?name="彭于晏"
)即扩展模板的变量
- 当我们最后一参数传入map时会以
key-value
的格式拼接在URL
后(通俗的说就是这样设置的变量会跟着URL
路径后面)
http://httpbin.org/post?name="彭于晏"
@TestvoidcontextLoads(){//请求地址String url ="http://httpbin.org/get";HashMap<String,String> map =newHashMap<>();
map.put("name","彭于晏");Object forObject = restTemplate.getForObject(url,Object.class, map);System.out.println(forObject);}
- 当我们传入简单的对象如String,Integer时且路径中有嵌入的占位符时就会代替调用URL中占位符
@TestvoidcontextLoads(){//请求地址String url ="http://httpbin.org/{2}/get";HashMap<String,String> map =newHashMap<>();Object forObject = restTemplate.getForObject(url,Object.class,99);System.out.println(forObject);}
参考
spring cloud 做微服务时关于RestTemplate中的各种请求方法的使用总结_DWT_CCFK的博客-CSDN博客
RestTemplate 详解 - 知乎 (zhihu.com)
RestTemplate使用教程 - 简书 (jianshu.com)
-End-
版权归原作者 不爱吃胡萝卜的阿丁 所有, 如有侵权,请联系我们删除。