okhttp3 get请求支持Body

前言:

好久没有写博客了,现在已经做程序员8年了。。。改天再专门写这些废话吧,哈哈。言归正传。

问题:

okhttp3 的get请求如何支持Body?

回答这个问题之前,首先http协议并不建议在get请求加body,okhttp不支持get请求加body,意思是既然要加body,直接用post就好了。

HttpURLConnection支持get加body的,今天不谈,今天之谈okhttp。

直接说,由于各种不可抗力因素导致,必须在get请求上加body,那么okhttp如何实现呢?

经过深入阅读okhttp源码,并且打断点,得出如下代码:

此代码是demo代码,写的粗糙请见谅


 #为了okhttp支持GET RequestBody
 -keepclassmembers public class okhttp3.Request {
    *** method;
  }


/**
 * 让okhttp get请求支持body
 */
public class FixGetWithBody {
    public static final String HEADER_KEY = "real_method";
    public static final String HEADER_VALUE = "GET";
    public static final String HEADER = HEADER_KEY + ": " + HEADER_VALUE;
    static ThreadLocal<Request> sThreadLocal = new ThreadLocal<>();
// TODO 第一步,在请求的时候,使用POST方式构造request,并且header里增加 HEADER_KEY,HEADER_VALUE,用来表示实际走GET请求
    /**
     * 添加到okhttp里
     *
     * @return
     */
    public static Interceptor getInterceptor() {
        return new InterceptorImpl();
    }

    /**
     * 添加到okhttp里
     */
    public static EventListener getEventListener() {
        return new EventListenerImpl();
    }

    private static boolean needConvertToGet(Request request) {
        if (request == null) {
            return false;
        }
        return HEADER_VALUE.equalsIgnoreCase(request.header(HEADER_KEY));
    }

    private static void changeMethod(String method) {
        Request request = sThreadLocal.get();
        if (request != null) {
            try {
                Field field = Request.class.getDeclaredField("method");
                field.setAccessible(true);
                field.set(request, method);
            } catch (IllegalAccessException | NoSuchFieldException e) {
            }
        }
    }

    private static class EventListenerImpl extends EventListener {

        @Override
        public void requestHeadersStart(Call call) {
            super.requestHeadersStart(call);
            // 在发送header之前,改回GET,这一步为了让服务端收到GET请求
            changeMethod("GET");
        }

        @Override
        public void requestHeadersEnd(Call call, Request req) {
            super.requestHeadersEnd(call, req);
            // 为了让okhttp发送body,需要改为post,跳过get校验(因为post请求OKHTTP底层才发送body)
            // 不过此时header已经发送完成,改回POST也无妨。
            changeMethod("POST");
            
            sThreadLocal.remove();
        }
    }

    private static class InterceptorImpl implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            RealInterceptorChain realChain = (RealInterceptorChain) chain;
            Request request = realChain.request();
            if (needConvertToGet(request)) {
                request = request.newBuilder().removeHeader(HEADER_KEY).build();
                sThreadLocal.set(request);
            }
            return chain.proceed(request);
        }
    }
}

okhttp分析过程

其他不用看,只看我写的注释就行了


OKHTTP源码CallServerInterceptor类


public final class CallServerInterceptor implements Interceptor {
    private final boolean forWebSocket;

    public CallServerInterceptor(boolean forWebSocket) {
        this.forWebSocket = forWebSocket;
    }

    public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain)chain;
        HttpCodec httpCodec = realChain.httpStream();
        StreamAllocation streamAllocation = realChain.streamAllocation();
        RealConnection connection = (RealConnection)realChain.connection();
        Request request = realChain.request();
        long sentRequestMillis = System.currentTimeMillis();
// 在requestHeadersStart内部改为GET,告诉服务端是此次请求是GET请求,GET这个标记是在writeRequestHeaders方法发送的。
        realChain.eventListener().requestHeadersStart(realChain.call());
        httpCodec.writeRequestHeaders(request);
// 在requestHeadersEnd内部改为POST,为了保证下一步的permitsRequestBody校验通过。实际上GET标记已经发送给服务端了,此时改为POST有无妨
        realChain.eventListener().requestHeadersEnd(realChain.call(), request);
        Builder responseBuilder = null;
        if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
            if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
                httpCodec.flushRequest();
                realChain.eventListener().responseHeadersStart(realChain.call());
                responseBuilder = httpCodec.readResponseHeaders(true);
            }

            if (responseBuilder == null) {
                realChain.eventListener().requestBodyStart(realChain.call());
                long contentLength = request.body().contentLength();
                CallServerInterceptor.CountingSink requestBodyOut = new CallServerInterceptor.CountingSink(httpCodec.createRequestBody(request, contentLength));
                BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
                request.body().writeTo(bufferedRequestBody);
                bufferedRequestBody.close();
                realChain.eventListener().requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
            } else if (!connection.isMultiplexed()) {
                streamAllocation.noNewStreams();
            }
        }

        httpCodec.finishRequest();
        if (responseBuilder == null) {
            realChain.eventListener().responseHeadersStart(realChain.call());
            responseBuilder = httpCodec.readResponseHeaders(false);
        }

        Response response = responseBuilder.request(request).handshake(streamAllocation.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();
        int code = response.code();
        if (code == 100) {
            responseBuilder = httpCodec.readResponseHeaders(false);
            response = responseBuilder.request(request).handshake(streamAllocation.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();
            code = response.code();
        }

        realChain.eventListener().responseHeadersEnd(realChain.call(), response);
        if (this.forWebSocket && code == 101) {
            response = response.newBuilder().body(Util.EMPTY_RESPONSE).build();
        } else {
            response = response.newBuilder().body(httpCodec.openResponseBody(response)).build();
        }

        if ("close".equalsIgnoreCase(response.request().header("Connection")) || "close".equalsIgnoreCase(response.header("Connection"))) {
            streamAllocation.noNewStreams();
        }

        if ((code == 204 || code == 205) && response.body().contentLength() > 0L) {
            throw new ProtocolException("HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
        } else {
            return response;
        }
    }

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值