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和 ...
随机推荐
- Java servlet和JSP的区别和联系
Java servlet技术:在Java代码中嵌入HTML JSP技术:HTML输出时比较便捷,就在HTML中嵌入Java代码 Java servlet技术:擅长编写Java代码 JSP技术:擅长页面 ...
- 对于富文本编辑器中使用lazyload图片懒加载
使用lazyload.js图片懒加载的作用是给用户一个好的浏览体验,同时对服务器减轻了压力,当用户浏览到该图片的时候再对图片进行加载,项目中使用lazyload的时候需要将图片加入data-orgin ...
- <数据结构系列3>队列的实现与变形(循环队列)
数据结构第三课了,今天我们再介绍一种很常见的线性表——队列 就像它的名字,队列这种数据结构就如同生活中的排队一样,队首出队,队尾进队.以下一段是百度百科中对队列的解释: 队列是一种特殊的线性表,特殊之 ...
- 20个python项目--图片转字符画
转自实验楼:https://www.shiyanlou.com/courses/370/learning/?id=1191 代码: # -*- coding:utf-8 -*- from PIL im ...
- 使用ocelot作为api网关
新建网站项目然后添加ocelot 的nuget包 新建ocelot.json的网关的配置文件 { "GlobalConfiguration": { "BaseUrl&qu ...
- vue父子组件相互传值的实例
当子组件需要向父组件传递数据时,就要用到自定义事件 子组件用 $emit()来触发事件,父组件用$on()来监昕子组件的事件 父组件也可以直接在子组件的自定义标签上使用 v-on 来监昕子组件触发的自 ...
- python 并发编程 多进程 生产者消费者模型介绍
一 生产者消费者模型介绍 为什么要使用生产者消费者模型 生产者指的是生产数据的任务,消费者指的是处理数据的任务, 生产数据目的,是为了给消费者处理. 在并发编程中,如果生产者处理速度很快,而消费者处理 ...
- 【6.18校内test】T1多项式输出
日常题前废话: 首先so amazing 的一件事,因为在洛谷上立下了的flag,然后这次考试前两道题都是刚刚做过不久的题emmm(相当于白送200吗qwq,但是这阻挡不了我第三题不会的脚步qwq) ...
- Android引用多媒体
res目录下,创建raw目录(Android会自动识别这个目录),如果自己创建的目录,可能无效底下的mp3格式,mp4格式的文件名必须小写. 引用方式: mediaPlayer = MediaPlay ...
- 项目常用的几个mysql函数
1.find_in_set函数 find_in_set(str,strlist); str是一个字符串 strlist是字符串列表--一个有多个子链被“,”分开的字符串 有多种情况: a.str为nu ...