okhttp配置读取RequestBody的Interceptor可能无法请求问题

最近调试需要,给okhttp添加了一个日志Interceptor,代码是参考源码的日志Interceptor,所以应该没有问题。但是,在上传文件时,发现请求发送失败。

首先分析一下okhttp的请求过程:
RealCall的调用采用chain模式。chain(RealInterceptorChain)开始是用户配置的应用Interceptor。之后是内置Interceptor,以完成最终数据处理并返回,包括缓存Interceptor、连接Interceptor。RealCall在内置Interceptor添加后,添加用户的网络Interceptor,这也就是为何只有网络Interceptor才有connection。因为用户的网络Interceptor是在内置Interceptor之后,而这时已经通过连接Interceptor(ConnectInterceptor)创建tcp。最后的Interceptor是CallServerInterceptor,实现数据发送和接收,并返回数据。
另外,根据RealInterceptorChain的proceed可以知道,网络Interceptor只能调用chain一次。

然后分析一下Request、Response接口:
request和response对数据的读写都是基于buffer。request发送文件时,会使用流对象。response读取数据都是基于流(毕竟不会连接后就读取全部数据)。response的source是基础封装,其它接口都是通过source.buffer来获取数据。source.request是读取数据的接口,需调用后,才会缓存到source.buffer。所以,在源码的日志Interceptor里面,对response的body输出,首先是获取source,然后读取(下载网络数据),之后使用clone(浅拷贝,数据共享的)去输出日志。那么request呢?源码里面没有发现可以对requet内容进行修改的接口(基本是final,且没有setter),只能重新创建request,但是这个又不符合拦截需要。

根据以上的分析可以知道,在没有其它用户Interceptor情况下,对request的body读取是在最后的CallServerInterceptor。这时即使body是一次性的流对象也不会出现问题。但是,若用户自己添加了Interceptor,同时读取了这个流对象,那么CallServerInterceptor如何再获取这部分数据?问题已经很清晰:Interceptor里面可以对request和response进行处理,但是body处理要注意是否可以重新读取问题。

request的body有多种类型,除了byte[]重复读取不会有问题外,其它都是只能读取一次,例如File、Inputstream。Interceptor需要读取request的body,只能通过反射覆盖body(自己读取后,再封装成byte[]配置回去),因为那些字段都是final(目前我的处理方案,或者有官方方案)。若Interceptor需要读取response的body,需要对source.buffer进行clone或者记得skip回开始,否则其它使用者是无法再读取数据的。

// Must clone request body which cannot read again such as file!!
// But the byte[] request body can read again!!
Buffer buffer = new Buffer();
requestBody.writeTo(buffer);
// Change request body!!
// Its clone or copy is shared!!
RequestBody cloneRequestBody = RequestBody.create(requestBody.contentType(), buffer.clone().readByteArray());
replaceRequestBody(request, cloneRequestBody);
public static void replaceRequestBody(Request request, RequestBody cloneRequestBody) {
	try {
		Field f = Request.class.getDeclaredField("body");
		f.setAccessible(true);
		f.set(request, cloneRequestBody);
	} catch (Exception e) {
		Logger.t(TAG, "replace request body error", e);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值