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里面处理。

results matching ""

    No results matching ""