OkHttp调用chain.proceed时出现连接关闭异常该如何解决?
异常根因
OkHttp 的 ResponseBody 是绑定了网络IO流的一次性可消费资源,只要调用过 bytes()、string()、charStream() 等消费方法,或是主动调用了 close() 方法,底层流就会被永久关闭,后续再尝试读取就会抛出 IllegalStateException: closed 异常。从你提供的异常栈可以明确看到,报错触发点是拦截器中调用了 ResponseBody.bytes() 方法消费了响应体,后续下游逻辑再读响应体时就触发了该异常。
你修改方法参数类型的操作对解决该问题没有任何作用,这是OkHttp响应流的设计规则,和参数类名无关。
解决方法
如果需要在拦截器中读取响应体做校验,不能直接消费原始响应的body,需要先读取body的副本,避免影响下游使用。OkHttp 提供了 peekBody() 方法可以直接返回响应体的拷贝,读取拷贝不会影响原始响应的流状态。
同时注意不要在拦截器中捕获所有异常后返回null,OkHttp要求拦截器必须返回合法的Response对象,返回null会引发额外的空指针异常。
修改后的参考代码如下:
@Slf4j @RequiredArgsConstructor public class AuthHeaderInterceptor implements Interceptor { private final String headerKey; private final String headerValue; @Override public Response intercept(final Interceptor.Chain chain) throws IOException { final Request requestWithAuthHeader = chain.request().newBuilder() .header(this.headerKey, this.headerValue) .build(); final Response originalResponse = chain.proceed(requestWithAuthHeader); // 读取响应头不会触发流关闭,可正常操作 final String signatureHeader = originalResponse.headers().get("x-signature"); log.info("x-signature={}", signatureHeader); // 读取响应体要操作拷贝的副本,不影响原始响应流 ResponseBody copiedBody = originalResponse.peekBody(Long.MAX_VALUE); // 在这里做你的body校验逻辑,比如读取bytes或者string byte[] bodyBytes = copiedBody.bytes(); log.info("响应体长度:{}", bodyBytes.length); // 直接返回原始响应即可,下游仍可正常读取body return originalResponse; } }
注意事项
如果你的响应体体积特别大,peekBody() 会将全量响应体加载到内存中,可能引发OOM风险,这种场景建议改用流式读取校验的方案,无需加载全量body到内存。
内容的提问来源于stack exchange,提问作者BarbetNL
相关产品推荐
相关产品推荐

