今天在做一个跨域请求的时候遇到一个问题,我需要同时从JDK7版本的项目接口中取出设置的请求头和响应的二进制流数据,按着源码分析了半天,把API倒是玩明白了,最后发现是因为没有注意可变形参引发的问题。。因为之前没有用过这个API,所以出这个问题的时候也没往这方面想。。。记录一下吧
1.环境模拟
1.1接口提供
提供一个接口,设置一个请求头给response,把图片二进制流写入response
@GetMapping("/pic1")
public void getPic(HttpServletRequest request,HttpServletResponse response) throws IOException {
response.setHeader("keyy","valuee");
System.out.println(response.getHeader("keyy"));
File file = new File("C:\\Users\\zhangjiahao\\Desktop\\变更图片\\班级活动.jpg");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
byte[] buf = new byte[2048];
ServletOutputStream os = response.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(os);
while(bis.read(buf) != -1){
bos.write(buf);
}
bos.flush();
bos.close();
bis.close();
}
1.2工具类(2个)
前提须知:
getForEntity()
和exchange()
本质上都是调用的excute()
返回值都是ResponseEntity
产生的问题是,这个ResponseEntity最后在getBody()
的时候,一个能读取到二进制流,一个读取不到
1.2.1使用getForEntity()
public static String getStreamAndHeader(String url,String headKey,HttpServletResponse response) {
RestTemplate restTemplate = getSingleRestTemplate();
ResponseEntity<byte[]> responseEntity = restTemplate.getForEntity(url, null, byte[].class, (Object) null);
byte[] res = responseEntity.getBody();//获取不到。res=null
HttpHeaders headers = responseEntity.getHeaders();
String value = headers.getFirst(headKey);
try {
ServletOutputStream os = response.getOutputStream();
os.write(res);
os.flush();
os.close();
}
catch (IOException e) {
logger.error("请求接口二进制流写入response时异常{}",e);
}
return value;
}
1.2.2使用exchange()
public static void getStreamWithNoParam(String url, HttpMethod method, HttpServletResponse response) {
RestTemplate restTemplate = getSingleRestTemplate();
try {
ResponseEntity<byte[]> exchange = restTemplate.exchange(url, method, null, byte[].class, (Object) null);
byte[] body = exchange.getBody();//正常获取
String value = exchange.getHeaders().getFirst("keyy");
response.setHeader("keyy",value);
response.getOutputStream().write(body);
}
catch (IOException e) {
logger.error("RestTemplateUtils获取接口二进制流异常");
}
}
2.源码分析思路
2.1方法本身区别
调用exchange()的时候,内部调用的方法是需要携带请求参数的,不过可以设为null
两个方法本身的区别就是:requestEntity
,即请求参数
2.2分别看两个内部的方法
2.2.1 exchange-带参的:httpEntityCallback
到目前为止没有看到对返回值response进行操作的内容
2.2.2getFroEntity-无参:acceptHeaderRequestCallback
3.到此没有分析出异常,则看request回调相关的execute
3.1分析理由
因为两个方法的区别就是在于回调RequestCallback
,本身分析不出来问题,就去找其他RequestCallback
相关的即execute()
执行方法本身
3.2doExecute()
doWithRequest()对于这两个方法来说,其实都是会执行的
debug的时候能正常进去
虽然这里没有指定requestEntity
但是内部造了一个requestCallBack,和requestEntity没有关系
4.定位到doWithRequest()
这两个实现类其实是父子类关系,都会调用(内部用的super)
4.1 debug 发现支持的媒体类型
发现在使用getForEntity()的时候,走到这里是false,因此没有指定返回值的类型
而正常正确的应该是这样
4.2回去看参数是否正确
发现这里给responseType指定为了null,那么问题来了,为什么参数写错了但是没提示?
这是因为最后最后一个形参是Object... uriVariables
,这种形参放在最后一位就支持这种很多逗号的写法
5.修改参数
然后就跑通了
6.结论
其实这两个方法本质只有“是否携带请求参数”的区别,其他的所有东西都一样,执行的过程一样,返回值一样,返回值的使用方式一样。
唯一值得需要注意的是,以后遇到这种最后一个形参是Object... uriVariables
的,就要对形参的个数比较敏感一点