1. OkHttp添加Interceptor
OkHttpClient提供了两个添加Interceptor的方法,addInterceptor和addNetworkInterceptor。根据上一篇OkHttp拦截器Interceptor里面提到的流程,这两个方法添加的Interceptor执行的时间不同,前者是在最开始最前头的位置,此时还未建立connection,而后者是在已经建立了connection之后,再被执行。
1.1. addInterceptor
static OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request req = chain.request();
// 重新创建RequestBody
RequestBody body = getBody("a", "bcd");
Request newReq = req.newBuilder()
.post(body).build();
return chain.proceed(newReq);
}
})
.build();
private static void getUsers() {
Request req = new Request.Builder()
.post(getBody("a", "bb")) // 原参数a
.url("http://10.0.2.2:3000/users2")
.build();
try {
okhttp3.Response response = httpClient.newCall(req).execute();
Log.e(TAG, "getUsers: " + response);
} catch (IOException e) {
Log.e(TAG, "getUsers: " + e);
}
}
private static RequestBody getBody(String key, String value) {
try {
final JSONObject object = new JSONObject();
object.put(key, value);
RequestBody body = RequestBody.create(MediaType.parse("application/json"), object.toString());
return body;
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
上面的代码通过addInterceptor添加了一个Interceptor,在该Interceptor里面重新创建了一个RequestBody并将它放到Request里面,在请求发到服务端时,服务端接收到的参数a就不是bb了而是bcd。
{ a: 'bcd' }
headers = {"content-type":"application/json; charset=utf-8","content-length":"11","host":"10.0.2.2:3000","connection":"Keep-Alive","accept-encoding":"gzip","user-agent":"okhttp/3.12.0"}
POST /users2 200 4030.154 ms - 9
上面是服务端控制台输出的内容。
1.2. addNetworkInterceptor
将上面的addInterceptor方法改成addNetworkInterceptor,其它数据操作不变,会发现服务端报了一个异常。
POST /users2 - - ms - -
SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
Unexpected end of JSON input,即json字符串意外中止了,服务端未正确拿到客户端传递的json字符串,猜测是Content-Length不对,再来看一下BridgeInterceptor的intercept方法。
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
long contentLength = body.contentLength();
if (contentLength != -1) {
// 指定 Content-Length
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
可以看出在BridgeInterceptor.intercept方法中,已经指定了Content-Length,而通过addNetworkInterceptor方法添加的Interceptor是在BridgeInterceptor的后面被执行,在addNetworkInterceptor中添加的Interceptor修改了RequestBody,而没有重新修改计算Content-Length,服务端拿不到正确的Content-Length,导致服务端报异常。
修改上面addInterceptor里面的Interceptor处理逻辑,重新计算并指定Content-Length,上面所述的问题即解决。
static OkHttpClient httpClient = new OkHttpClient.Builder()
.addNetworkInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request req = chain.request();
RequestBody body = getBody("a", "bcd");
long contentLength = body.contentLength();
Request newReq = req.newBuilder()
.post(body)
.header("Content-Length", Long.toString(contentLength))
.build();
return chain.proceed(newReq);
}
})
.build();
此时服务端的日志输出为
{ a: 'bcd' }
headers = {"content-type":"application/json; charset=utf-8","host":"10.0.2.2:3000","connection":"Keep-Alive","accept-encoding":"gzip","user-agent":"okhttp/3.12.0","content-length":"11"}
POST /users2 200 4006.079 ms - 9
除了上面的修改方法,也可以通过指定Transfer-Encoding来修复。
static OkHttpClient httpClient = new OkHttpClient.Builder()
.addNetworkInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request req = chain.request();
RequestBody body = getBody("a", "bcd");
Request newReq = req.newBuilder()
.post(body)
.header("Transfer-Encoding", "chunked")
.removeHeader("Content-Length")
.build();
return chain.proceed(newReq);
}
})
.build();
此时服务端的输出为
{ a: 'bcd' }
headers = {"content-type":"application/json; charset=utf-8","host":"10.0.2.2:3000","connection":"Keep-Alive","accept-encoding":"gzip","user-agent":"okhttp/3.12.0","transfer-encoding":"chunked"}
POST /users2 200 4006.387 ms - 9
上面两种方式,都不是正确的使用,只是为了表述addInterceptor和addNetworkInterceptor的区别,涉及到有修改RequestBody的,应该在addInterceptor里面处理。