Okhttp拦截器统一异常处理并多次读取response.body().string()
参考: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()的更多相关文章
- 从网络请求过程看OkHttp拦截器
前言 之前我们结合设计模式简单说了下OkHttp的大体流程,今天就继续说说它的核心部分--拦截器. 因为拦截器组成的链其实是完成了网络通信的整个流程,所以我们今天就从这个角度说说各拦截器的功能. 首先 ...
- SpringMVC——自定义拦截器、异常处理以及父子容器配置
自定义拦截器: 一.若想实现自定义拦截器,需要实现 org.springframework.web.servlet.HandlerInterceptor 接口. 二.HandlerIntercepto ...
- JavaEE开发之SpringMVC中的自定义拦截器及异常处理
上篇博客我们聊了<JavaEE开发之SpringMVC中的路由配置及参数传递详解>,本篇博客我们就聊一下自定义拦截器的实现.以及使用ModelAndView对象将Controller的值加 ...
- SpringMVC(四)-- 文件下载、自定义拦截器、异常处理
1.文件下载 用ResponseEntity<byte[]> 返回值完成文件下载 具体参见本博客之前的<文件上传下载> @RequestMapping(value=" ...
- SpringBoot系统列 4 - 常用注解、拦截器、异常处理
在前面代码基础上进行改造: 1.SpringBoot常用注解 @SpringBootApplication :指定SpringBoot项目启动的入口,是一个复合注解,由@Configuration.@ ...
- SpringMVC入门一:基础知识(依赖、注解、文件上传/下载、拦截器、异常处理等)
为了使Spring可插入MVC架构,SpringFrameWork在Spring基础上开发SpringMVC框架,从而使用Spring进行WEB开发时可以选择使用Spring的SpringMVC框架作 ...
- SpringMVC拦截器与异常处理
点击查看上一章 在我们SpringMVC中也可以使用拦截器对用户的请求进行拦截,用户可以自定义拦截器来实现特定的功能.自定义拦截器必须要实现HandlerInterceptor接口 package c ...
- OkHttp拦截器的实现原理
今天项目中遇到需要将从push接收到的数据按照协议parse成应用层需要的结构化数据类型问题:因为push消息类型繁多,等待解析出的结构化数据类型也多样,有的还需要经过几步的parse过程:而且因为项 ...
- axios封装,使用拦截器统一处理接口
1.项目路径下,引入axios.qs依赖 npm install axios npm install qs 2.在项目的src路径下新建一个commJs文件夹,在commJs文件夹里新建aps.js和 ...
随机推荐
- springboot之RocketMq实现
环境:win10 1.下载安装包 http://mirrors.tuna.tsinghua.edu.cn/apache/rocketmq/4.4.0/rocketmq-all-4.4.0-bin-re ...
- 阶段3 3.SpringMVC·_01.SpringMVC概述及入门案例_06.入门案例的流程总结
配置了load-on-startup等于1 表示启动了服务器就会去创建DispatcherServlet 如果不配置load-on-startup为1 那么第一次发送请求才会去创建Dispatcher ...
- ATP检测 BAPI BAPI_MATERIAL_AVAILABILITY
*****ATP检测 DATA: END_RLT LIKE EBAN-LFDAT, NOT_AVAILABLE, AVAILABLE( ...
- frei0r-20190715-118a589 编译 frei0r 时注意事项
如果滤镜是 CPP 编写,需要选择 gcc Thread model: win32 模式,如果选择 posix 模式时,提示错误: 无法找到 dll 文件: frei0r-1.6.1-dlls-201 ...
- 64位编译器下,将指针转换成UINT32,不需要修改编译选项的编码方式
一些严格的64位编译器,将指针转换成UINT32,会报各种丢失精度的错误. 但很显然,有些时候,我们就是需要转换,且并不会真正丢失精度. 此时不需要修改编译选项的编码方式,有些用处了 示例如下: un ...
- wpf 虚拟键盘 对外部程序
对外部程序,以记事本为例,xaml中设置模拟按键的控件 Focusable="False": /// <summary> /// 发送按键 /// </summa ...
- Ubuntu虚拟机共享文件夹的1234
第一 在虚拟机内添加路径 进入虚拟机软件,点开工具栏上方虚拟机,点击设置,选择选项,查看共享文件夹,点击添加,下一步 第二: 第三 点击启用此共享 点击完成 第四 查看共享的文件 在mnt里可以看到S ...
- Linux内核基础优化
Linux内核基础优化 net.ipv4.ip_forward = 1 #开启网络转发 net.ipv4.conf.default.rp_filter = 0 #开启代理功能 net.ipv4.con ...
- Objective-C中的自动释放池
自动释放池块@autoreleasepool 自动释放池块在MRC和ARC下都可以使用.在MARC下,为了将自动释放池块内部的变量放入自动释放池,需要手动调用autorelease方法:在ARC下,只 ...
- Oracle数据库跟踪存储过程
1.[右击]该存储过程 ——>勾选[添加调试信息]——>点击[测试]选项 2.输入对应参数,点击下图左上角红框按钮,开始跟踪,可逐语句逐过程跟踪