前言
RestTemplate 简化了发起 HTTP 请求以及处理响应的过程,并且支持 REST的工具
微信公众平台 微信公众平台是运营者通过公众号为微信用户提供资讯和服务的平台,而公众平台开发接口则是提供服务的基础,开发者在公众平台网站中创建公众号、获取接口权限后,可以通过本接来帮助开发。
然后真的想吐槽一下,微信的接口,是真的乱.
- 说好的返回Json 然后返回类型是
text/plain
- 获取素材有用get请求,有的用post请求
- 一会儿
http
,一会儿https
其他的坑慢慢爬吧
有兴趣可以关注一下我的微信接口项目 hoody-wechat-springboot-starter 微信公众号API
RestTemplate介绍
本篇内容基于Spring 官方文档 RestTemplate 加上个人理解/实践进行介绍
如有错误请留言,我会尽快修改的
初始化
1.常规方式
RestTemplate template = new RestTemplate();
2.如果要使用Apache HttpComponents
作为请求库
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
主要方法
RestTemplate
方法 | 描述 |
---|---|
getForEntity | 发起GET 请求,返回响应体ResponseEntity<T> |
getForObject | 发起GET 请求,返回响应内容<T> body |
postForEntity | 发起POST 请求,返回响应体ResponseEntity<T> |
postForObject | 发起POST 请求,返回响应内容<T> body |
exchange | 上述方法的更通用(且不太固定)的版本,在需要时提供额外的灵活性。它接受一个RequestEntity(包括HTTP方法,URL,标题和正文作为输入)并返回一个ResponseEntity。 |
execute | 执行请求的最通用方式,通过回调接口完全控制请求准备和响应提取 |
getForObject的使用
此方法有3个重载
1.public
以可变参数
接收uri参数
RestTemplate template = new RestTemplate();
String result = template.getForObject(
"https://example.com/hotels/{hotel}/bookings/{booking}",
String.class,
"42", "21"); //restTemplate会对参数进行URI编码
2.public
以Map
形式接收uri参数
RestTemplate template = new RestTemplate();
Map<String, String> vars = Collections.singletonMap("hotel", "42");
Hotel hotel = restTemplate.getForObject(
"https://example.com/hotels/{hotel}/rooms/{hotel}", Hotel.class, vars);
//restTemplate会对参数进行URI编码
3.public
以URI
对象作为参数
RestTemplate template = new RestTemplate();
//自定义Uri实例
String result = restTemplate.getForObject(yourUri,String.class)
getForEntity 的使用
此方法有3个重载 方法参数同
getForObject
唯一区别是返回值是ResponseEntity<T>
类型ResponseEntity<T>
包含了HTTP响应的头信息header
- 通过header可以获取到响应的头信息,比如
status 状态码
,ContentType 响应类型
等等 body
则是响应数据通过HttpMessageConverter
转换的数据.
对ResponseEntity<T>
进行操作
//其他重载参数同 getForObject
ResponseEntity<Hotel> entity= restTemplate.getForObject(
"https://example.com/hotels/{hotel}/rooms/{hotel}", Hotel.class, "super8");
//获取返回值
Hotel body = entity.getBody();
//获取响应类型
MediaType contentType = entity.getHeaders().getContentType();
//获取响应状态码
HttpStatus statusCode = entity.getStatusCode();
postForObject 的使用
此方法有3个重载
与getForObject
相比多了一个Object request
参数
其他参数作用和getForObject
相同
Object request
可以使用以下值作为参数
- org.springframework.util.MultiValueMap
- org.springframework.http.HttpEntity
以 public
T postForObject(String url, @Nullable Object request, Class responseType, Object... uriVariables) 方法举例
通过HttpEntity
携带JSON
参数,并明确指定请求头的Content-type
RestTemplate template = new RestTemplate();
//JSON String
String param = "{\"type\":\"student\"}";
//create request header 创建请求头
HttpHeaders headers = new HttpHeaders()
headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON_UTF8)
//创建请求参数
HttpEntity<String> entity = new HttpEntity<String>(param, headers)
//调用
String result = new RestTemplate().postForObject(
"https://example.com/{class}/user", //String url
entity, //Object request
String.class, //Class<T> responseType
"1") //Object... uriVariables
通过MultiValueMap
携带多个参数
在大多数情况下,您不必为每个部件指定Content-Type。内容类型是根据HttpMessageConverter所选内容类型自动确定的,不指定
Content-Type
以便基于文件扩展名的情况下进行自动选择。
RestTemplate template = new RestTemplate();
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
//字符串值
parts.add("fieldPart", "fieldValue");
//图片文件
parts.add("filePart", new FileSystemResource("...logo.png"));
//json
parts.add("jsonPart", new Person("Jason"));
// 手动指定类型,并添加xml
String xmlValue = "<user><name>hoody</name><sex>male</sex></user>";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
parts.add("xmlPart", new HttpEntity<>(xmlValue, headers));
String result = new RestTemplate().postForObject(
"https://example.com/{class}/user", //String url
parts, //Object request
String.class, //Class<T> responseType
"1") //Object... uriVariables
postForEntity的使用
此方法有3个重载 方法参数同
postForObject
唯一区别是返回值是ResponseEntity类型 ResponseEntity 包含了HTTP响应的头信息header
通过HttpEntity
携带JSON
参数,并明确指定请求头的Content-type
举例
RestTemplate template = new RestTemplate();
//JSON String
String param = "{\"type\":\"student\"}";
//create request header 创建请求头
HttpHeaders headers = new HttpHeaders()
headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON_UTF8)
//创建请求参数
HttpEntity<String> entity = new HttpEntity<String>(param, headers)
//调用
ResponseEntity<String> result = new RestTemplate().postForObject(
"https://example.com/{class}/user", //String url
entity, //Object request
String.class, //Class<T> responseType
"1") //Object... uriVariables
//获取返回值
String body = entity.getBody();
//获取响应类型
MediaType contentType = entity.getHeaders().getContentType();
//获取响应状态码
HttpStatus statusCode = entity.getStatusCode();
exchange 的使用
返回值是
ResponseEntity<T>
类型
与postForEntity
和getForEntity
的参数基本相同
主要的的区别:
1.可以以org.springframework.http.HttpMethod枚举作为参数,指定请求方法(GET
,POST
,DELETE
等等)
2.可以给GET
请求添加请求头信息
其他还有一些官方文档也没多写,
主要作用是 使用RestTemplate作为工具,写自定义REST组件时,可以自由控制请求方式
例子:
使用exchange 发起GET
请求
设置请求头header
:
- 设置user-agent
- 设置接收数据类型为JSON
RestTemplate template = new RestTemplate();
//创建header
HttpHeaders headers = new HttpHeaders();
//设置user-agent
headers.add(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36");
//添加接收数据媒体类型,JSON
List<MediaType> acceptableMediaTypes = new ArrayList<>();
acceptableMediaTypes.add(MediaType.APPLICATION_JSON_UTF8)
headers.setAccept(acceptableMediaTypes)
//创建请求体
HttpEntity<HttpHeaders> entity = new HttpEntity<>(headers)
//发出请求
ResponseEntity<String> responseEntity = restTemplate.exchange(
"https://example.com/hotels/{hotel}/bookings/{booking}",
HttpMethod.GET,
entity,
String.class,
"42","21"
)
//获取返回值
String body = entity.getBody();
//获取响应类型
MediaType contentType = entity.getHeaders().getContentType();
//获取响应状态码
HttpStatus statusCode = entity.getStatusCode();
execute 介绍
一般情况下,使用上述方法就够了
execute
方法是所有上述请求方法最后都会调用execute
方法
查看源码可以发现 getForXXX
,postForXXX
的最后都是execute
@Override
public <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request,
Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
//最后都会调用`execute`方法
return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables));
}
getForXXX
,postForXXX
方法内部主要做了这几件事:
- 使用request参数和responseType创建了一个
RequestCallback
,它会根据responseType
设置header
的acceptType,并且将request参数组织requestBody
- 使用responseType参数创建了一个ResponseExtractor,它会根据
responseType
自动采用合适的HttpMessageConverter
对返回值进行转型
当然我们也可以直接调用execute
,使用自己的RequestCallback
, ResponseExtractor
实现
比如这个例子,这个是我做微信接口时遇到的 下载永久素材时:
如果下载的是图片,音频等微信会直接返回文件流
如果下载的是视频或者素材id错误时,会返回文本信息download_url
或error_code
//素材ID
String mediaId = "xxxxxxxxxxx"
//微信 api 地址
String url = "https://api.weixin.qq.com/cgi-bin/material/get_material?access_token=${getAccessToken()}"
// 请求参数,要求以JSON格式发送
String json = "{\"media_id\":\"${mediaId}\"}"
//创建请求头,加入json
HttpHeaders headers = new HttpHeaders()
headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON_UTF8)
HttpEntity<String> httpEntity = new HttpEntity<String>(json, headers)
//使用RestTemplate提供的方法创建RequestCallback
RequestCallback requestCallback = restTemplate.httpEntityCallback(httpEntity)
// 自定义返回值处理器
ResponseExtractor<File> responseExtractor =
new ResponseExtractor<File>() {
@Override
File extractData(ClientHttpResponse response) throws IOException {
//判断响应媒体类型是否是文本
if (response.getHeaders().getContentType() == org.springframework.http.MediaType.TEXT_PLAIN) {
//获取输入流,并转为String
InputStream inputStream = response.getBody()
byte[] bytes = new byte[inputStream.available()]
inputStream.read(bytes)
String str = new String(bytes)
//转为JSON对象
JSONObject result = new JSONObject(str)
//判断微信接口是否返回错误
if (!result.isNull("errcode")) {
println(result.toString())
throw new WechatMediaException("get Media fail :${result.toString()}")
}
//获取视频下载地址
String videoUrl = new JSONObject(str).getString("down_url")
//下载视频方法
return downloadVideo(videoUrl)
}
//如果是其他类型
else {
InputStream inputStream = response.getBody()
//获取响应文件名
String filename = response.getHeaders().getContentDisposition().getFilename()
//使用响应文件名创建临时文件
File file = File.createTempFile(
filename.substring(0, filename.lastIndexOf(".")),
filename.substring(filename.lastIndexOf("."), filename.length())
)
file.deleteOnExit()
//输入流写入临时文件
FileOutputStream fileOutputStream = new FileOutputStream(file)
byte[] bytes = new byte[1024]
int ch
while ((ch = inputStream.read(bytes)) > -1) {
fileOutputStream.write(bytes, 0, ch);
}
fileOutputStream.close();
//返回文件给调用对象
return file
}
}
}
//调用execute
File mediaFile = restTemplate.execute(url, HttpMethod.GET,
requestCallback, responseExtractor)
HttpMessageConverter
通过上表方法进行HTTP请求时,会传入一个参数Class<T> responseType
,指定返回值的类型,此类型作为<T> body
返回
一般情况下,直接指定响应类型Class即可
RestTemplate会根据响应类型ContentType
和Class
选择合适的转化器
流程图:
类型转换默认包含:
转化器 | 描述 |
---|---|
StringHttpMessageConverter | 以String.class 类型从HTTP请求和响应中读取和写入实例的实现。默认情况下,此转换器支持所有文字媒体类型(text/*),并用写Content-Type的text/plain。 |
FormHttpMessageConverter | 以MultiValueMap<String, String> 类型从HTTP请求和响应中读取和写入表单数据的实现。默认情况下,此转换器读取和写入 application/x-www-form-urlencoded媒体类型。 |
ByteArrayHttpMessageConverter | 以byte[].class 从HTTP请求和响应中读取和写入字节数组的实现。默认情况下,该转换器支持所有媒体类型(/),并用写Content-Type的application/octet-stream。您可以通过设置supportedMediaTypes属性和覆盖来覆盖它getContentType(byte[])。 |
MarshallingHttpMessageConverter | 通过使用Spring Marshaller和包中的Unmarshaller抽象来读写XML 的org.springframework.oxm实现。该转换器需要先加入Marshaller和Unmarshaller才可以使用。您可以通过构造函数或bean属性注入这些。默认情况下,此转换器支持 text/xml和application/xml。 |
MappingJackson2HttpMessageConverter | 使用Jackson的读写JSON的实现 ObjectMapper。您可以根据需要通过使用Jackson提供的注释来自定义JSON映射。当您需要进一步控制时(对于需要为特定类型提供自定义JSON序列化器/反序列化器的情况),您可以注入一个自定义ObjectMapper ,默认情况下,此转换器支持application/json |
MappingJackson2XmlHttpMessageConverter | 使用Jackson XML扩展来读写XML 的实现 XmlMapper。您可以根据需要通过使用JAXB或Jackson提供的注释来自定义XML映射。当您需要进一步控制时(对于需要为特定类型提供自定义XML序列化器/反序列化器的情况),您可以注入一个自定义XmlMapper 。默认情况下,此转换器支持application/xml |
SourceHttpMessageConverter | 以javax.xml.transform.Source从HTTP请求和响应中读取和写入 。只支持DOMSource ,SAXSource ,StreamSource 。默认情况下,此转换器支持 text/xml和application/xml |
BufferedImageHttpMessageConverter | 可以从HTTP请求和响应中读取和写入java.awt.image.BufferedImage。支持使用Java I/O API进行读写操作。 |
实现自定义HttpMessageConverter
1.继承org.springframework.http.converter.AbstractHttpMessageConverter
注意重写以下几个方法
getSupportedMediaTypes
: 返回自定义转化器支持的媒体类型 ContentTyperead
: 读取http 响应并组织需要返回的对象。write
: 解析支持的对象类型数据,并将数据写入到response中。
其他方法酌情重写
2.将自定义转化器 添加进默认 HttpMessageConverter 列表
@Configuration
public class MVCConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new TestMessageConverter());
}
}
完
转载请保留我的名字和原文地址:http://www.hoody.tech/blog/detail/32