前言:
好久没有写博客了,现在已经做程序员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;
}
}