博客
关于我
使用RestTemplate,显示请求信息,响应信息
阅读量:579 次
发布时间:2019-03-11

本文共 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 -> {                    List
values = 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 不能读第二次这个很要命呀

问题找到了,初步的想到了几种解决

  1. 改写代码,不close流,读取完之后再reset
  2. 代理一下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方法。很明显了它不允许随意修改读取定位。没办法只转为第二种方法了。

方法二:代理,每次都返回一个新的流

  1. 静态代理实现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();        }    }}

运行效果:

代码运行没问题,但是总感觉代码写出来笨笨的,要重写这么多用不着的方法,看着不舒服,换个写法。

  1. 动态代理
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了)
  • 静态代理的代码有点啰嗦,动态代理又有点不够味

转载地址:http://lnttz.baihongyu.com/

你可能感兴趣的文章
python 加密算法及其相关模块的学习(hashlib,RSA,random,string,math)
查看>>
JavaSE总结
查看>>
手动造轮子——基于.NetCore的RPC框架DotNetCoreRpc
查看>>
Python IO编程
查看>>
CSS入门总结
查看>>
使用 TortoiseGit 时,报 Access denied 错误
查看>>
基于 HTML5 WebGL 的污水处理厂泵站自控系统
查看>>
[系列] Go gRPC 调试工具
查看>>
django-表单之模型表单渲染(六)
查看>>
c++之程序流程控制
查看>>
yarn出现“There are no scenarios ; must have at least one"
查看>>
spring-boot-2.0.3之redis缓存实现,不是你想的那样哦!
查看>>
httprunner学习23-加解密
查看>>
有道云笔记 同步到我的博客园
查看>>
李笑来必读书籍整理
查看>>
http头部 Expect
查看>>
Hadoop(十六)之使用Combiner优化MapReduce
查看>>
《机器学习Python实现_10_06_集成学习_boosting_gbdt分类实现》
查看>>
CoreCLR源码探索(八) JIT的工作原理(详解篇)
查看>>
IOS开发Swift笔记16-错误处理
查看>>