Retrofit和Okhttp添加cookie - 掘金
使用CookieJar把cookie添加到请求中之前看过OkHttp的源码知道CookieJar,所以就使用它来添加请求中的cookie,首先定义一个CookieJar,代码如下:
class AnalysisCookie : CookieJar {
override fun saveFromResponse(url: HttpUrl, cookies: MutableList) {
}
override fun loadForRequest(url: HttpUrl): MutableList {
val cookies = ArrayList()
val token = SharedPreferenceUtil.getStringSPAttrs(ContextUtil.getAppContext(), SharedPreferenceUtil.AttrInfo.USER_TOKEN, "")
token.doThis {
cookies.add(Cookie.Builder()
.domain("your domain")
.path("/")
.name("your cookieKey")
.value("your cookieValue")
.httpOnly()
.secure()
.build())
}
return cookies
}
}
然后在OkHttpClient生成的过程中把我们的CookieJar添加进去
val okHttpBuilder = OkHttpClient.Builder()
.connectTimeout(TIME_OUT, TimeUnit.SECONDS)
.readTimeout(TIME_READ, TimeUnit.SECONDS)
.writeTimeout(TIME_WRITE, TimeUnit.SECONDS)
.cookieJar(AnalysisCookie())
简单两步就可以把cookie添加到请求中了。
什么是CookieJar什么是CookieJar呢,单从名字就可以看出CookieJar是跟cookie相关的,那么CookieJar是在哪里定义的呢?在OkHttp的源码中会找到它的蛛丝马迹
OkHttpClient(Builder builder) {
......
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
......
}
CookieJar是如何生效的
那么CookieJar是怎么生效的呢?先看下OkHttp的简单使用
OkHttpClient okHttpBuilder = OkHttpClient.Builder();
final Request request = new Request.Builder().url("https://github.com/").build();
Response response = client.newCall(request).execute();
上面只是粗略的写法,仅供大家查看,在实际应用过程中还会添加很多的配置。从上面代码中发现,最终会执行OkHttpClient的newCall(request).execute()方法,本文不详细分析OkHttp的详细源码就不管execute()里面的逻辑了,让我们看一下newCall里面做了什么。
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
从上面会发现最终返回一个RealCall,那么RealCall又是什么呢?写到这里发现源码还是很多(手动狗头),说好不execute的最后发现还是避不开😭。让我们看看,下面是RealCall的execute()方法源码
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
从上面会发现最终请求的Response是从这段代码中返回的,
Response result = getResponseWithInterceptorChain();
细心的同学可能都发现了,到目前为止跟我们配置的CookieJar根本没关系啊,甚至OkHttpClient里面配置的东西也都没见到啊,别着急马上就是见证奇迹的时刻了,让我们看一下getResponseWithInterceptorChain()究竟做了什么。
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List interceptors = new ArrayList();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
看到这里相信大家都有了眉目,别的不多说主要看这句
interceptors.add(new BridgeInterceptor(client.cookieJar()));
最终CookieJar会被加入到定义的BridgeInterceptor中,而BridgeInterceptor跟平时使用时添加的什么LoggerInterceptor之类的拦截器是一种东西,都是在请求进行之前添加自己的操作,让我们快到斩乱麻,看一下BridgeInterceptor究竟对CookieJar做了什么
@Override public Response intercept(Chain chain) throws IOException {
......
List cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
......
return responseBuilder.build();
}
最终CookieJar会被添加到请求的Header中,
/** Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}. */
private String cookieHeader(List cookies) {
StringBuilder cookieHeader = new StringBuilder();
for (int i = 0, size = cookies.size(); i < size; i++) {
if (i > 0) {
cookieHeader.append("; ");
}
Cookie cookie = cookies.get(i);
cookieHeader.append(cookie.name()).append('=').append(cookie.value());
}
return cookieHeader.toString();
}
至此,cookie已经被添加到请求中了。
从CookieJar的使用思路中得到的另外一种添加cookie的方法从上面代码中发现,cookie最终是以 builder.addHeader("Cookie", cookie)的方式添加到请求中的,那么我们可不可以不使用CookieJar,自己实现一个Interceptor直接添加呢?话不多说,直接撸码
class CookieInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().newBuilder()
val cookie = "cookieName=cookieValue"
builder.addHeader("Cookie", cookie)
return chain.proceed(builder.build())
}
}
经过测试发现这种方式也是可行的,其实CookieJar只是对上面这种方式进行了封装,在具体使用时大家可以根据喜好才选择自己喜欢的方式。
到此本文也就写完了,看源码的过程总是比较累的,但是好处也是巨大的。知其然知其所以然,看的过程中也能学到很多东西,扩大我们的眼界。
Cookie持久化:https://www.jianshu.com/p/f1df12ceaedc
可以使用工具类:在zhy封装的okhttpUtils项目里的类
https://github.com/hongyangAndroid/okhttputils/blob/master/okhttputils/src/main/java/com/zhy/http/okhttp/cookie/store/MemoryCookieStore.java
1、SerializableHttpCookie(持久化到本地sp文件中)
2、MemoryCookieStore(持久化在内存中)