OkHttp3.14.9添加日志拦截器触发Stream closed异常如何解决
OkHttp 3.14.x ResponseBody 多次消费异常修复方案
错误原因
- OkHttp 的
ResponseBody底层封装了网络请求的输入流,调用string()/bytes()等方法消费内容后,流会被自动关闭,后续业务逻辑再读取 body 就会抛出Stream closed异常 - 你代码中直接读取了 response body 内容后直接返回原 response,相当于把已经消费过的 body 交给后续逻辑,因此报错
修复方案
有两种常用实现方式,可按需选择:
方案1:使用 peekBody 方法(推荐,代码简洁)
OkHttp 官方提供了 peekBody(long maxBytes) 方法,该方法会复制一份 body 的缓冲区内容返回,不会消费原始 response 的 body。
注意:peekBody 的入参是允许读取的最大字节数,若实际 body 长度超过该值,只会返回前对应长度的内容,不会影响原始 body 的完整性,建议根据业务场景设置合理值,避免大 body 导致 OOM
修复后的代码:
public class LoggingRequestInterceptor implements Interceptor { // 示例最大读取1M的body内容,可按需调整 private static final long MAX_LOG_BODY_SIZE = 1024 * 1024; @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Response response = chain.proceed(request); String responseBodyLog = response.body() == null ? null : response.peekBody(MAX_LOG_BODY_SIZE).string(); log.debug( "{}, {}, {}, {}, {}", request.url(), request.method(), request.body() == null ? null : JSONUtil.toJsonStr(request.body()), request.headers(), responseBodyLog ); return response; } }
方案2:手动构造新 Response 返回
如果需要自定义处理 body 的读取逻辑,也可以先把原始 body 的内容全部读出来,再重新构造一个新的 Response,把读出来的内容重新包装成 ResponseBody 设置回去,后续业务代码读取的是新构造的 body,不会受影响。
示例代码:
public class LoggingRequestInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Response response = chain.proceed(request); ResponseBody originalBody = response.body(); if (originalBody == null) { return response; } // 读取原始body内容打印日志 String bodyContent = originalBody.string(); log.debug( "{}, {}, {}, {}, {}", request.url(), request.method(), request.body() == null ? null : JSONUtil.toJsonStr(request.body()), request.headers(), bodyContent ); // 构造新的ResponseBody设置回Response返回 ResponseBody newBody = ResponseBody.create(originalBody.contentType(), bodyContent.getBytes(StandardCharsets.UTF_8)); return response.newBuilder() .body(newBody) .build(); } }
额外注意事项
- 如果请求是上传类的大 RequestBody,直接调用
JSONUtil.toJsonStr(request.body())也可能出现类似的流消费问题,建议同样对 RequestBody 做复制处理,或者仅对特定 contentType 的请求打印 body - 生产环境建议限制打印的 body 长度,避免大文件请求导致日志内容过大,占用过多磁盘空间或者触发 OOM
内容的提问来源于stack exchange,提问作者zack zhang
相关产品推荐
相关产品推荐

