参考:https://blog.csdn.net/a624806998/article/details/73863606

引言: 写这篇文章,因为在自己编写实现Http日志拦截器的时候,在拦截器中使用 response.body().string() 获取了返回的数据,但是在经过拦截器后,针对输出处理的时候,会再次调用 response.body().string(),这里就会导致流已关闭的异常。

参考上面的链接,修改了在拦截器中的response中的数据获取。保证了下一步的输出流的处理获取操作。

下面的就是能使用的的Http拦截器代码

package com.ztkj.common.log.interceptors;

import com.alibaba.fastjson.JSON;
import com.ztkj.common.log.aspect.bean.HttpHandlerBean;
import com.ztkj.common.log.aspect.bean.LinkTrackingBean;
import com.ztkj.common.log.aspect.threadlocals.LinkTrackingThreadLocal;
import okhttp3.*;
import okio.Buffer;
import okio.BufferedSource;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import java.io.EOFException;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.List;
import java.util.Map;
import java.util.UUID; /**
* okhttp 拦截器定义: 统计请求信息以及耗时
*
* @author hzb-ztkj
*/
public class HttpLogInterceptor implements Interceptor { private static final Logger logger = LoggerFactory.getLogger(HttpLogInterceptor.class); private static final Charset UTF8 = Charset.forName("UTF-8"); /**
* 拦截过程中 收集 http请求输入、输出等信息,日志输出打印
*
* @param chain
* @return
* @throws IOException
*/
@Override
public Response intercept(Chain chain) throws IOException { //获取当前请求
Request request = chain.request(); //根据请求类型分析
HttpHandlerBean httpHandlerBean = new HttpHandlerBean(); //从http request中获取请求信息
httpHandlerBean.setUrl(request.url().toString()); //设置请求头信息
Headers headers = request.headers();
Map<String, List<String>> stringListMap = headers.toMultimap();
httpHandlerBean.setParamHeaderStr(JSON.toJSONString(stringListMap)); //设置请求body信息
RequestBody requestBody = request.body();
if (requestBody != null) {
httpHandlerBean.setParamStr(getParam(requestBody));
} //设置请求方式
httpHandlerBean.setRequestMode(request.method()); Response response = null;
try {
response = chain.proceed(request); //设置状态码
httpHandlerBean.setCode(response.code() + ""); //设置响应头信息
Map<String, List<String>> responseHeaderMap = response.headers().toMultimap();
httpHandlerBean.setResultHeaderStr(JSON.toJSONString(responseHeaderMap)); //设置响应信息
ResponseBody responseBody = response.body();
if (responseBody != null) {
//httpHandlerBean.setResultStr(JSON.toJSONString(responseBody.string()));
httpHandlerBean.setResultStr(getResponseBody(responseBody));
} //设置用时信息
httpHandlerBean.setSendTime(response.sentRequestAtMillis());
httpHandlerBean.setReceiveTime(response.receivedResponseAtMillis()); //设置唯一性id
LinkTrackingBean linkTrackingBean = LinkTrackingThreadLocal.get();
if (linkTrackingBean == null) {
httpHandlerBean.setRequestId(UUID.randomUUID().toString());
} else {
httpHandlerBean.setRequestId(linkTrackingBean.getRequestId());
httpHandlerBean.setUserId(linkTrackingBean.getUserId());
httpHandlerBean.setProductId(linkTrackingBean.getProductId());
} //响应消息
httpHandlerBean.setMessage(response.message()); } catch (Exception e) {
logger.error("处理请求异常,异常原因:{}", ExceptionUtils.getStackTrace(e));
httpHandlerBean.setMessage(ExceptionUtils.getStackTrace(e));
} //输出,打印日志
logger.info(httpHandlerBean.httpLogPrint()); return response;
} private String getResponseBody(ResponseBody responseBody) throws Exception {
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE);
Buffer buffer = source.buffer(); Charset charset = UTF8;
MediaType contentType = responseBody.contentType();
if (contentType != null) {
try {
charset = contentType.charset(UTF8);
} catch (UnsupportedCharsetException e) {
logger.error("将http数据写入流异常,异常原因:{}", ExceptionUtils.getStackTrace(e));
}
} if (!isPlaintext(buffer)) {
return null;
} if (responseBody.contentLength() != 0) {
String result = buffer.clone().readString(charset);
return result;
}
return null;
} private boolean isPlaintext(Buffer buffer) throws EOFException {
try {
Buffer prefix = new Buffer();
long byteCount = buffer.size() < 64 ? buffer.size() : 64;
buffer.copyTo(prefix, 0, byteCount);
for (int i = 0; i < 16; i++) {
if (prefix.exhausted()) {
break;
}
int codePoint = prefix.readUtf8CodePoint();
if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
return false;
}
}
return true;
} catch (EOFException e) {
return false;
}
} /**
* 读取参数
*
* @param requestBody
* @return
*/
private String getParam(RequestBody requestBody) {
Buffer buffer = new Buffer();
String logparm;
try {
requestBody.writeTo(buffer);
logparm = buffer.readUtf8();
logparm = URLDecoder.decode(logparm, "utf-8");
} catch (IOException e) {
e.printStackTrace();
return "";
}
return logparm;
}
}

Okhttp拦截器统一异常处理并多次读取response.body().string()的更多相关文章

  1. 从网络请求过程看OkHttp拦截器

    前言 之前我们结合设计模式简单说了下OkHttp的大体流程,今天就继续说说它的核心部分--拦截器. 因为拦截器组成的链其实是完成了网络通信的整个流程,所以我们今天就从这个角度说说各拦截器的功能. 首先 ...

  2. SpringMVC——自定义拦截器、异常处理以及父子容器配置

    自定义拦截器: 一.若想实现自定义拦截器,需要实现 org.springframework.web.servlet.HandlerInterceptor 接口. 二.HandlerIntercepto ...

  3. JavaEE开发之SpringMVC中的自定义拦截器及异常处理

    上篇博客我们聊了<JavaEE开发之SpringMVC中的路由配置及参数传递详解>,本篇博客我们就聊一下自定义拦截器的实现.以及使用ModelAndView对象将Controller的值加 ...

  4. SpringMVC(四)-- 文件下载、自定义拦截器、异常处理

    1.文件下载 用ResponseEntity<byte[]> 返回值完成文件下载 具体参见本博客之前的<文件上传下载> @RequestMapping(value=" ...

  5. SpringBoot系统列 4 - 常用注解、拦截器、异常处理

    在前面代码基础上进行改造: 1.SpringBoot常用注解 @SpringBootApplication :指定SpringBoot项目启动的入口,是一个复合注解,由@Configuration.@ ...

  6. SpringMVC入门一:基础知识(依赖、注解、文件上传/下载、拦截器、异常处理等)

    为了使Spring可插入MVC架构,SpringFrameWork在Spring基础上开发SpringMVC框架,从而使用Spring进行WEB开发时可以选择使用Spring的SpringMVC框架作 ...

  7. SpringMVC拦截器与异常处理

    点击查看上一章 在我们SpringMVC中也可以使用拦截器对用户的请求进行拦截,用户可以自定义拦截器来实现特定的功能.自定义拦截器必须要实现HandlerInterceptor接口 package c ...

  8. OkHttp拦截器的实现原理

    今天项目中遇到需要将从push接收到的数据按照协议parse成应用层需要的结构化数据类型问题:因为push消息类型繁多,等待解析出的结构化数据类型也多样,有的还需要经过几步的parse过程:而且因为项 ...

  9. axios封装,使用拦截器统一处理接口

    1.项目路径下,引入axios.qs依赖 npm install axios npm install qs 2.在项目的src路径下新建一个commJs文件夹,在commJs文件夹里新建aps.js和 ...

随机推荐

  1. Android 中数据存储方式有哪些?

    a) 文件存储b) xml,SharedPreferencec) SQLite数据库d) ContentProvidere) 网络

  2. python之拷贝文件

    做了个小实验, 用于拷贝文件夹下面的jpg. 用于拓展, 可以引入类和方法, 拷贝你指定的任意类型的文件. import os src = 'C:\\Users\\Administrator\\Des ...

  3. beanFactory 设计模式 Bean 生命周期

    写在前面的话 适用读者:有一定经验的,本文不适合初学者,因为可能不能理解我在说什么 文章思路:不会一开始就像别的博客文章那样,Bean 的生命周期,源码解读(给你贴一大堆的源码).个人觉得应该由问题驱 ...

  4. linux环境下编译Android apk

    Android源码目录下的build/envsetup.sh文件,描述编译的命令 - m:       Makes from the top of the tree. - mm:      Build ...

  5. python关键字以及含义,用法

    Python常用的关键字   1.and , or and , or 为逻辑关系用语,Python具有短路逻辑,False and 返回 False 不执行后面的语句, True or 直接返回Tru ...

  6. C#静态调用带有SoapHeader验证的WebServices

    转自:http://blog.csdn.net/u012995964/article/details/54562111 本文记录带有SoapHeader验证的WebServices服务创建.部署及C# ...

  7. centos7 中如何查看、打开、关闭防火墙。

    首先是看centos7的防火墙的状态,查看的命令为: sudo systemctl status firewalld. 查看后,看到active(running)就意味着防火墙打开了, 如果想关闭防火 ...

  8. Maven从入门到精通(一)

    maven是一个项目管理工具,我的后续将会根据这个思维导图给大家由浅到深讲解Maven是如何管理我们的项目,以及我们如何更好的使用Maven maven在开发过程中功能主要有三个方面:   管理依赖 ...

  9. (4.13)mysql备份原理(转)

    关键词:mysqldump原理,--single-transaction,mysql备份原理 转自:https://www.cnblogs.com/cchust/p/5452557.html MySQ ...

  10. 思考--mysql 分库分表的思考

    查询不在分库键上怎么办,扫描所有库?由于分库了,每个库扫描很快?所以比单个表的扫描肯定快,可以这样理解吗. 多表jion怎么弄,把内层表发给每个分库吗? citus,tidb 都有这些问题,citus ...