
使用RestTemplate,显示请求信息,响应信息
发布日期:2021-05-14 18:22:14
浏览次数:8
分类:博客文章
本文共 10646 字,大约阅读时间需要 35 分钟。
使用RestTemplate,显示请求信息,响应信息
这里不讲怎么用RestTemplate具体细节用法,就是一个学习中的过程记录
一个简单的例子
public class App { public static void main(String[] args) { String url = "https://api.uixsj.cn/hitokoto/get"; RestTemplate restTemplate = new RestTemplate(); String body = restTemplate.getForObject(url, String.class); System.out.println(body); }}
运行结果:
❓:现在我想看看他的请求头,请求参数,响应头,响应体的详细信息是怎么样子的,这样也方便以后检查请求参数是否完整,响应正确与否。
经过搜集资料发现ClientHttpRequestInterceptor
满足需求,于是就有了下面的代码
打印请求头/响应头
public class App { public static void main(String[] args) { String url = "https://api.uixsj.cn/hitokoto/get"; RestTemplate restTemplate = new RestTemplate(); // 加上拦截器打印将请求请求,响应信息打印出来 restTemplate.setInterceptors(Collections.singletonList(new LoggingInterceptor())); String body = restTemplate.getForObject(url, String.class); System.out.println(body); }}@Slf4jclass LoggingInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { displayRequest(request, body); ClientHttpResponse response = execution.execute(request, body); displayResponse(response); return response; } /** * 显示请求相关信息 * @param request * @param body */ private void displayRequest(HttpRequest request, byte[] body) { log.debug("====request info===="); log.debug("URI : {}", request.getURI()); log.debug("Method : {}", request.getMethod()); log.debug("Req Headers : {}", this.headersToString(request.getHeaders())); log.debug("Request body: {}", body == null ? "" : new String(body, StandardCharsets.UTF_8)); } /** * 显示响应相关信息 * @param response * @throws IOException */ private void displayResponse(ClientHttpResponse response) throws IOException { StringBuilder inputStringBuilder = new StringBuilder(); try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), StandardCharsets.UTF_8))) { String line = bufferedReader.readLine(); while (line != null) { inputStringBuilder.append(line); inputStringBuilder.append('\n'); line = bufferedReader.readLine(); } } log.debug("====response info===="); log.debug("Status code : {}", response.getStatusCode()); log.debug("Status text : {}", response.getStatusText()); log.debug("Resp Headers : {}", headersToString(response.getHeaders())); log.debug("Response body: {}", inputStringBuilder.toString()); } /** * 将Http头信息格式化处理 * @param httpHeaders * @return */ private String headersToString(HttpHeaders httpHeaders) { if (Objects.isNull(httpHeaders)) { return "[]"; } return httpHeaders.entrySet().stream() .map(entry -> { Listvalues = entry.getValue(); return "\t" + entry.getKey() + ":" + (values.size() == 1 ? "\"" + values.get(0) + "\"" : values.stream().map(s -> "\"" + s + "\"").collect(Collectors.joining(", "))); }) .collect(Collectors.joining(", \n", "\n[\n", "\n]\n")); }}
运行结果:
执行过程中会报错,具体错误信息是
Exception in thread "main" org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://api.uixsj.cn/hitokoto/get": stream is closed; nested exception is java.io.IOException: stream is closed
这里报错信息是流已关闭
,报错是在添加LoggingInterceptor
后出现的,那就是新加代码引起的。在看看LoggingInterceptor
的实现,什么地方操作了流,并且关闭了流。
LoggingInterceptor.displayResponse
这个方法里面,为了读取响应体操作了流response.getBody()
,
try (...) {}// 这个try块结束后就把流给关了
注释掉代码中流操作相关代码,再次运行没有错误信息。因该是在拦截器后,RestTemplate也需要操作了response.getBody()
的流(废话)。
Response body 不能读第二次这个很要命呀
问题找到了,初步的想到了几种解决
- 改写代码,不
close
流,读取完之后再reset
流 - 代理一下
ClientHttpResponse
每次调用getBody
都返回一个新的输入流
解决不能重复读Response body
方法一:读取完后不关闭流
// 略...InputStream responseBody = response.getBody();BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(responseBody, StandardCharsets.UTF_8));String line = bufferedReader.readLine();while (line != null) { inputStringBuilder.append(line); inputStringBuilder.append('\n'); line = bufferedReader.readLine();}responseBody.reset();// 略...
很遗憾,执行后依旧有错误
Exception in thread "main" org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://api.uixsj.cn/hitokoto/get": mark/reset not supported; nested exception is java.io.IOException: mark/reset not supported
说的很清楚,不支持mark/reset
方法。很明显了它不允许随意修改读取定位。没办法只转为第二种方法了。
方法二:代理,每次都返回一个新的流
- 静态代理实现
ClientHttpResponse
接口,好在ClientHttpResponse
实现的接口数量不多,实现的代码如下。
@Slf4jclass LoggingInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { displayRequest(request, body); ClientHttpResponse response = execution.execute(request, body); // 包装代理一下 response = new ClientHttpResponseWrapper(response); displayResponse(response); return response; } /** * 显示请求相关信息 * @param request * @param body */ private void displayRequest(HttpRequest request, byte[] body) { // 略... } /** * 显示响应相关信息 * @param response * @throws IOException */ private void displayResponse(ClientHttpResponse response) throws IOException { StringBuilder inputStringBuilder = new StringBuilder(); try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), StandardCharsets.UTF_8))) { String line = bufferedReader.readLine(); while (line != null) { inputStringBuilder.append(line); inputStringBuilder.append('\n'); line = bufferedReader.readLine(); } } // 略... } /** * 将Http头信息格式化处理 * @param httpHeaders * @return */ private String headersToString(HttpHeaders httpHeaders) { // 略... } private class ClientHttpResponseWrapper implements ClientHttpResponse { private ClientHttpResponse clientHttpResponse; private byte[] body; public ClientHttpResponseWrapper(ClientHttpResponse clientHttpResponse) { this.clientHttpResponse = clientHttpResponse; } @Override public HttpStatus getStatusCode() throws IOException { return this.clientHttpResponse.getStatusCode(); } @Override public int getRawStatusCode() throws IOException { return this.clientHttpResponse.getRawStatusCode(); } @Override public String getStatusText() throws IOException { return this.clientHttpResponse.getStatusText(); } @Override public void close() { this.clientHttpResponse.close(); } /** * 缓存body每次返回一个新的输入流 * @return * @throws IOException */ @Override public InputStream getBody() throws IOException { if (Objects.isNull(this.body)) { this.body = StreamUtils.copyToByteArray(this.clientHttpResponse.getBody()); } return new ByteArrayInputStream(this.body == null ? new byte[0] : this.body); } @Override public HttpHeaders getHeaders() { return this.clientHttpResponse.getHeaders(); } }}
运行效果:
代码运行没问题,但是总感觉代码写出来笨笨的,要重写这么多用不着的方法,看着不舒服,换个写法。
- 动态代理
public class App { public static void main(String[] args) { String url = "https://api.uixsj.cn/hitokoto/get"; RestTemplate restTemplate = new RestTemplate(); restTemplate.setInterceptors(Collections.singletonList(new LoggingInterceptor())); String body = restTemplate.getForObject(url, String.class); System.out.println(body); }}@Slf4jclass LoggingInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { displayRequest(request, body); ClientHttpResponse response = execution.execute(request, body); // 包装代理一下 response = (ClientHttpResponse) Proxy.newProxyInstance(response.getClass().getClassLoader(), new Class[]{ClientHttpResponse.class}, new ClientHttpResponseHandler(response)); displayResponse(response); return response; } /** * 显示请求相关信息 * @param request * @param body */ private void displayRequest(HttpRequest request, byte[] body) { // 略...... } /** * 显示响应相关信息 * @param response * @throws IOException */ private void displayResponse(ClientHttpResponse response) throws IOException { StringBuilder inputStringBuilder = new StringBuilder(); try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), StandardCharsets.UTF_8))) { String line = bufferedReader.readLine(); while (line != null) { inputStringBuilder.append(line); inputStringBuilder.append('\n'); line = bufferedReader.readLine(); } } // 略...... } /** * 将Http头信息格式化处理 * @param httpHeaders * @return */ private String headersToString(HttpHeaders httpHeaders) { // 略...... } private static class ClientHttpResponseHandler implements InvocationHandler { private static final String methodName = "getBody"; private ClientHttpResponse clientHttpResponse; private byte[] body; ClientHttpResponseHandler(ClientHttpResponse clientHttpResponse) { this.clientHttpResponse = clientHttpResponse; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (StringUtils.equals(methodName, method.getName())) { if (Objects.isNull(this.body)) { this.body = StreamUtils.copyToByteArray(this.clientHttpResponse.getBody()); } return new ByteArrayInputStream(this.body == null ? new byte[0] : this.body); } return method.invoke(this.clientHttpResponse, args); } }}
运行结果:
总结
- 使用RestTemplate想要显示详细请求信息,和响应信息
- 添加拦截器
- 拦截器中操作InputSteam导致流关闭,不能重复读Response body
- 尝试不关闭流,重置流的方案失败
- 使用代理解决 Response body 不能读第二次读的问题
- 静态代理(可以重复读Response body了)
- 动态代理(可以重复读Response body了)
- 静态代理的代码有点啰嗦,动态代理又有点不够味
发表评论
最新留言
感谢大佬
[***.8.128.20]2025年04月11日 19时08分24秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
layer.confirm 无效
2019-03-11
Java 回调机制
2019-03-11
7、回归和特征选择
2019-03-11
pycharm使用(新建工程、字体修改、调试)
2019-03-11
什么是Numpy、Numpy教程
2019-03-11
Python学习笔记——元组
2019-03-11
异常声音检测
2019-03-11
PCB学习笔记——AD17如何添加新的封装
2019-03-11
numpy版本问题
2019-03-11
无法打开文件“opencv_world330d.lib”的解决办法
2019-03-11
maven项目通过Eclipse上传到svn上面,再导入到本地出现指定的类找不到的问题
2019-03-11
maven 项目部署到tomcat下 没有class文件
2019-03-11
算法训练 未名湖边的烦恼(递归,递推)
2019-03-11
算法训练 完数(循环,数学知识)
2019-03-11
什么是接口
2019-03-11
2020版nodejs12.18.3安装配置教程
2019-03-11
iview组件库中,Form组件里的Input,无法正确绑定on-enter事件
2019-03-11