先说个大概的结构:
doExecute(),层层向外封装exchange()适合用来封装成工具类,他的返回值是ResponseEntitypostForObject() 和 postForEntity本质上一样,只是后者对运行结果进行了封装,返回的也是ResponseEntity,类似于lambda表达式中的Option类1.本文的源码分析部分采用“从外到内”的顺序进行分析
2.类似postForObject和getForObject这种的区别仅仅是GET请求和POST请求的区别,本文仅分析POST请求的
3.postFroLocation()方法是返回一个URI,不是本文的重点
看源码的注释部分,几乎一致,也就是两个方法基本没有什么区别
其中一个返回值是封装类ResponseEntity

重要!!:关于提取响应的方式
postForObject:HttpMessageConvertExtractor
postforEntity:ResponseExtractor
这两个Extractor类都有一个公共接口extractData,他在execute方法执行的时候被调用,用于获取HTTP响应的数据,只是在实现上,一个只获取响应体,另一个还要额外获取headers和status


既然这个封装类有状态码status,响应头headers,那么一定在某个地方,getForObject没有去指定而getForEntity指定了(这时候应该猜到是extractData()方法,将在后面讨论)



直接获取上面两个参数,不再赘述

这里直接看getForEntity对应的ResponseEntityResponseExtractor,即封装了status和headers的执行过程

因此,只有返回值是Entity的才有headers和status,否则像getForObject这种方法返回的只是一个响应体

即两个都会调用的,上面那么打红框的,通篇都是只针对了响应体进行操作,没有涉及任何的headers和status

如果是封装为Entity,那么还要再加一层判断方法
到此为止我们分析了为什么getForObject()返回的只有响应体,而getForEntity()返回的包括了响应体 响应头 响应状态
而getForEntity()和exchange()返回的都是ResponseEntity对象,因此第二点引出二者的区别
get和post请求只有一个区别,后续会演示如何使用API
requestEntity请求参数

这里找了一个写的比较全的API演示:参考链接
请求的接口有两个@RequestParam参数,通过姓名和性别获取图片的一个例子
ResponseEntity responseEntity =
restTemplate.getForEntity("http://localhost:8080/pic/pic1?name={name}&sex={sex}", byte[].class,
"名字", "性别");
ServletOutputStream os = response.getOutputStream();
os.write(responseEntity.getBody());
当然对于@RequestParam最好的做法是放在最后一个形参中,传一个map,在后面会提到
大同小异
restTemplate.getForEntity("http://localhost:8080/pic/pic1/{name}/{sex}", byte[].class,
"名字", "性别");
当本地有dto代码的时候,第二个形参直接用dto就好
//当本地有dto的代码时,可以直接用dto来作为第二个请求体形参
People people = new People();
people.setAge(22);
people.setName("张三");
people.setTall(181);
//最后一个可变数组的形参不用指定
ResponseEntity res =
restTemplate.postForEntity("http://localhost:8080/pic/people/pic1", people, byte[].class);
ServletOutputStream os = response.getOutputStream();
os.write(res.getBody());
但不一定是任何时候都有这个dto代码,如果没有这个dto代码,去调用其他的服务器,还需要写一个dto吗?不需要
使用HashMap虽然在语法上没有问题**,但是会导致请求体对应不上去**,被请求的接口收不到请求体参数
例如:
HashMap hashMap = new HashMap<>();
hashMap.put("name","张三");
hashMap.put("age","1");
hashMap.put("tall","181");
//最后一个可变数组的形参不用指定
ResponseEntity res =
restTemplate.postForEntity("http://localhost:8080/pic/people/pic1", hashMap, byte[].class);
ServletOutputStream os = response.getOutputStream();
os.write(res.getBody());
MultiValueMap
他是org.springframework.util包下的一个Map
他兼容了类型匹配,与SpringMVC的适配性很好
MultiValueMap map = new LinkedMultiValueMap();
map.add("name","张三");
map.add("age","11");
map.add("tall","181");
//最后一个可变数组的形参不用指定
ResponseEntity res =
restTemplate.postForEntity("http://localhost:8080/pic/people/pic1", map, byte[].class);
ServletOutputStream os = response.getOutputStream();
os.write(res.getBody());
请求体写在MultiValueMap
请求头写在HttpHeaders
最终用HttpEntity封装,需要注意泛型的指定就是MultiValueMap
MultiValueMap map = new LinkedMultiValueMap();
map.add("name","张三");
map.add("age","11");
map.add("tall","181");
HttpHeaders httpHeaders = new HttpHeaders();
//按需求自行添加
// httpHeaders.setContentType();
// httpHeaders.setExpires();
//这个Entity包含了请求体 和 请求头
//泛型的指定即:map的类型
HttpEntity> entity = new HttpEntity<>(map,httpHeaders);
//最后一个可变数组的形参不用指定
ResponseEntity res =
restTemplate.postForEntity("http://localhost:8080/pic/people/pic1", entity, byte[].class);
ServletOutputStream os = response.getOutputStream();
os.write(res.getBody());
有两种写法,一种是url拼接,利用最后一个参数是可变数组
ResponseEntity res = restTemplate.exchange("http://localhost:8080/pic/withbody?param1={param1}", HttpMethod.POST,
httpEntity, byte[].class, "请求参数1"
);
第二种是最后一个参数用一个map
HashMap urls = new HashMap<>();
urls.put("param1","测试1");
ResponseEntity res = restTemplate.exchange("http://localhost:8080/pic/withbody", HttpMethod.POST,
httpEntity, byte[].class, urls
);
baseByteTractor和baseJsonTractor进行封装import java.io.BufferedOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
/**
* @Description RestTemplate工具类
* 1.getByte() postByte()是用来下载流的,因此形参必须有HttpResponseBody
* 2.getJson() postJson()是用来获取json的,形参可以不带response
* 3.每个方法最后一个形参requireBodyOnly默认true,即只需要响应体,一般建议设为true,
* 否则可能造成因header自动解析产生的问题
* 4.post请求可以额外携带请求体
* 5.可能产生EOFException异常,正常,可以全局捕获日志记录
*
* @Author zjh
* @Date 17:04 2022/7/28
**/
public class RestTemplateUtils {
private static Logger logger = LoggerFactory.getLogger("RestLogger");
private volatile static RestTemplate restTemplate;
/**
* RestTemplate单例 懒汉 双检锁
**/
public static RestTemplate getSingleRestTemplate() {
if (restTemplate == null) {
synchronized (RestTemplateUtils.class) {
if (restTemplate == null) {
restTemplate = new RestTemplate();
}
}
}
return restTemplate;
}
/**
* 基础方法,下载文件二进制流到response输出流
*
* @param url 不包含请求参数 即 ?param={}
* @param method POST或GET
* @param requestHeaders 请求头,可为空
* @param requestParams 请求参数,可为空
* @param requestBody 请求体,可为空
* @param requireBodyOnly 是否是只需要响应体,默认true,true:只需要响应体 false:同时返回响应状态 响应头 响应体
* @return void
**/
public static void baseByteTractor(String url, HttpMethod method,
@Nullable Map requestHeaders,
@Nullable Map requestParams,
@Nullable Map requestBody,
HttpServletResponse response,
@Nullable Boolean requireBodyOnly) {
if (requireBodyOnly == null) {
requireBodyOnly = true;
}
MultiValueMap params = new LinkedMultiValueMap();
MultiValueMap body = new LinkedMultiValueMap();
HttpHeaders headers = new HttpHeaders();
//1.设置请求参数
if (requestParams != null && !requestParams.isEmpty()) {
Iterator> paramsIterator = requestParams.entrySet().iterator();
Entry nextParam = null;
while (paramsIterator.hasNext()) {
nextParam = paramsIterator.next();
params.add(nextParam.getKey(), nextParam.getValue());
}
}
//2.设置请求头
if (requestHeaders != null && !requestHeaders.isEmpty()) {
Iterator> headersIterator = requestHeaders.entrySet().iterator();
Entry nextHeader = null;
while (headersIterator.hasNext()) {
nextHeader = headersIterator.next();
headers.add(nextHeader.getKey(), nextHeader.getValue());
}
}
//3.设置请求体
if (requestBody != null && !requestBody.isEmpty()) {
Iterator> bodyIterator = requestBody.entrySet().iterator();
Entry bodyNext = null;
while (bodyIterator.hasNext()) {
bodyNext = bodyIterator.next();
body.add(bodyNext.getKey(), bodyNext.getValue());
}
}
//4.请求体 请求头封装到HttpEntity
HttpEntity> entity = new HttpEntity<>(body, headers);
//5.执行
RestTemplate restTemplate = getSingleRestTemplate();
BufferedOutputStream bos = null;
try {
ResponseEntity exchange = restTemplate.exchange(url, method, entity, byte[].class, params);
if (!requireBodyOnly && response != null) {
//响应状态码
response.setStatus(exchange.getStatusCodeValue());
//响应头,可能存在一个key对应多个value,本方法中会将同名header合并
HttpHeaders resHeaders = exchange.getHeaders();
Iterator>> resHeadersIterator = resHeaders.entrySet().iterator();
while (resHeadersIterator.hasNext()) {
Entry> headersNext = resHeadersIterator.next();
response.setHeader(headersNext.getKey(), headersNext.getValue().toString());
}
}
//响应体
ServletOutputStream os = response.getOutputStream();
bos = new BufferedOutputStream(os);
byte[] buf = exchange.getBody();
bos.write(buf);
bos.flush();
} catch (IOException e) {
logger.error("RestTemplateUtils获取接口二进制流异常");
} catch (RestClientException e) {
logger.error("远程调用接口异常{}", e);
} finally {
try {
bos.close();
} catch (IOException e) {
logger.error("RestTemplateUtils流关闭异常");
}
}
}
/**
* 基础方法,请求接口,返回JSON
*
* @param url 不包含请求参数 即 ?param={}
* @param method POST或GET
* @param requestHeaders 请求头,可为空
* @param requestParams 请求参数,可为空
* @param requestBody 请求体,可为空
* @param requireBodyOnly 是否是只需要响应体,默认true,true:只需要响应体 false:同时返回响应状态 响应头 响应体
* @return String Json
**/
public static String baseJsonTracktor(String url, HttpMethod method,
@Nullable Map requestHeaders,
@Nullable Map requestParams,
@Nullable Map requestBody,
@Nullable HttpServletResponse response,
@Nullable Boolean requireBodyOnly) {
if (requireBodyOnly == null) {
requireBodyOnly = true;
}
MultiValueMap params = new LinkedMultiValueMap();
MultiValueMap body = new LinkedMultiValueMap();
HttpHeaders headers = new HttpHeaders();
//1.设置请求参数
if (requestParams != null && !requestParams.isEmpty()) {
Iterator> paramsIterator = requestParams.entrySet().iterator();
Entry nextParam = null;
while (paramsIterator.hasNext()) {
nextParam = paramsIterator.next();
params.add(nextParam.getKey(), nextParam.getValue());
}
}
//2.设置请求头
if (requestHeaders != null && !requestHeaders.isEmpty()) {
Iterator> headersIterator = requestHeaders.entrySet().iterator();
Entry nextHeader = null;
while (headersIterator.hasNext()) {
nextHeader = headersIterator.next();
headers.add(nextHeader.getKey(), nextHeader.getValue());
}
}
//3.设置请求体
if (requestBody != null && !requestBody.isEmpty()) {
Iterator> bodyIterator = requestBody.entrySet().iterator();
Entry bodyNext = null;
while (bodyIterator.hasNext()) {
bodyNext = bodyIterator.next();
body.add(bodyNext.getKey(), bodyNext.getValue());
}
}
//4.请求体 请求头封装到HttpEntity
HttpEntity> entity = new HttpEntity<>(body, headers);
//5.执行
RestTemplate restTemplate = getSingleRestTemplate();
String bodyJson = "";
try {
ResponseEntity exchange = restTemplate.exchange(url, method, entity, String.class, params);
if (!requireBodyOnly && response != null) {
//响应状态码
response.setStatus(exchange.getStatusCodeValue());
//响应头,可能存在一个key对应多个value,本方法中会将同名header合并
HttpHeaders resHeaders = exchange.getHeaders();
Iterator>> resHeadersIterator = resHeaders.entrySet().iterator();
while (resHeadersIterator.hasNext()) {
Entry> headersNext = resHeadersIterator.next();
response.setHeader(headersNext.getKey(), headersNext.getValue().toString());
}
}
bodyJson = exchange.getBody();
} catch (RestClientException e) {
logger.error("远程调用接口异常{}", e);
}
return bodyJson;
}
/**
* GET请求 下载流
*
* @param url 不包含请求参数 即 ?param={}
* @param requestParams 请求参数,可为空
* @param requireBodyOnly 是否是只需要响应体,默认true,true:只需要响应体 false:同时返回响应状态 响应头 响应体
* @return void
**/
public static void getByte(String url, HttpServletResponse response,
@Nullable Map requestParams,
@Nullable Boolean requireBodyOnly) {
if (requireBodyOnly == null) {
requireBodyOnly = true;
}
baseByteTractor(url, HttpMethod.GET, null, requestParams, null, response, requireBodyOnly);
}
/**
* POST请求 下载流
*
* @param url 不包含请求参数 即 ?param={}
* @param requestParams 请求参数,可为空
* @param requestBody 请求体,可为空
* @param requireBodyOnly 是否是只需要响应体,默认true,true:只需要响应体 false:同时返回响应状态 响应头 响应体
* @return void
**/
public static void postByte(String url, HttpServletResponse response,
@Nullable Map requestParams,
@Nullable Map requestBody,
@Nullable Boolean requireBodyOnly) {
if (requireBodyOnly == null) {
requireBodyOnly = true;
}
baseByteTractor(url, HttpMethod.POST, null, requestParams, requestBody, response, requireBodyOnly);
}
/**
* GET请求 获取JSON
*
* @param url 不包含请求参数 即 ?param={}
* @param requestParams 请求参数,可为空
* @param requireBodyOnly 是否是只需要响应体,默认true,true:只需要响应体 false:同时返回响应状态 响应头 响应体
* @return void
**/
public static String getJson(String url,
@Nullable HttpServletResponse response,
@Nullable Map requestParams,
@Nullable Boolean requireBodyOnly) {
if (requireBodyOnly == null) {
requireBodyOnly = true;
}
String resJson = baseJsonTracktor(url, HttpMethod.GET, null, requestParams, null, response, requireBodyOnly);
return resJson;
}
/**
* POST请求 获取JSON
*
* @param url 不包含请求参数 即 ?param={}
* @param requestParams 请求参数,可为空
* @param requestBody 请求体,可为空
* @param requireBodyOnly 是否是只需要响应体,默认true,true:只需要响应体 false:同时返回响应状态 响应头 响应体
* @return void
**/
public static String postJson(String url,
@Nullable HttpServletResponse response,
@Nullable Map requestParams,
@Nullable Map requestBody,
@Nullable Boolean requireBodyOnly) {
if (requireBodyOnly == null) {
requireBodyOnly = true;
}
String resJson = baseJsonTracktor(url, HttpMethod.POST, null, requestParams, requestBody, response, requireBodyOnly);
return resJson;
}
}

