参考: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. Word模板替换

    package com.sisa.auweb.tools.bookmarkprocess; import org.apache.poi.openxml4j.opc.OPCPackage; import ...

  2. 三十七:数据库之SQLAlchemy外建之多对多关系

    准备工作 from sqlalchemy import create_engine, Column, Integer, String, Float, Text, ForeignKeyfrom sqla ...

  3. c++面向对象 —— 类和对象

    类和对象 类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法.类中的数据和方法称为类的成员.函数在一个类中被称为类的成员. 一.类定义 实际上并没有定义任何数据,但它定义了类的名称意味着什么 ...

  4. vue父子组件相互传值的实例

    当子组件需要向父组件传递数据时,就要用到自定义事件 子组件用 $emit()来触发事件,父组件用$on()来监昕子组件的事件 父组件也可以直接在子组件的自定义标签上使用 v-on 来监昕子组件触发的自 ...

  5. linux chgrp 只改文件目录的 属组

    chgrp 组 文件或目录 [root@MongoDB ~]# chgrp incahome test.sh [root@MongoDB ~]# ll total -rw-------. root r ...

  6. redis漏洞攻击

    参考: https://www.cnblogs.com/kobexffx/p/11000337.html 利用redis漏洞获得root权限,挖矿. 解决方法: 用普通帐号启redis,用云的redi ...

  7. mapper中通过resultMap自定义查询结果映射

    mybatis中使用resultType做自动映射时,要注意字段名和pojo的属性名必须一致,若不一致,则需要给字段起别名,保证别名与属性名一致. 使用resultMap做自定义结果映射,字段名可以不 ...

  8. [luogu 3175] [HAOI2015]按位或(min-max容斥+高维前缀和)

    [luogu 3175] [HAOI2015]按位或 题面 刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2^n-1]的数字,与你手上的数字进行按位或运算.问期望多少秒后,你手上的数字变成2^n ...

  9. Spring Boot解决无法访问图片的问题

    找了很多Spring Boot项目访问图片的解决方式,发现都是配置的,有时配置了也没有用.然后自己研究了一种简单操作的方法. 1,在Spring Boot的static目录下创建一个新目录img(或者 ...

  10. Head First PHP&MySQl第三章代码

    addemail.html <!DOCTYPE html> <html lang="cn"> <head> <meta charset=& ...