您的位置:首页 > 理论基础 > 计算机网络

Retrofit——Cookie

2016-04-27 20:40 363 查看
今天学习了有关http的cookie知识后,刚好想结合retrofit来试验一下学习成果,于是拿了别人的接口来做实验。

要想获取cookie,那么肯定有个入口区提供cookie,一般都是在app应用的第一次访问时候(如登录或者登录前的验证),由服务器通过响应头来返回的,然后客户端获取到cookie后再以后的访问中加入header中进行访问。

fiddler抓包

用工具抓包,获取接口信息,大概信息如下:
http://[b]*****[/b]/updateToken


这个就是服务端返回的信息,cookie在响应头中,一般用浏览器抓包时,会发现访问接口的时候,在请求头中竟然会出现cookie的配置,那么它是从哪来的呢,别怕,这是因为你之前在该浏览器中已经访问过了,它做了记录,所以有了之前的cookie。解决如下:

1.删除浏览器的cookie(经测试,没有成功,估计姿势不对)

2.用手机模拟请求,并且抓包(就是用的这种)

好了,第一次请求的返回头已经有了,那么在retrofit该如何获取呢?

获取cookie

用retrofit当然得写拦截器,先贴上GetCookiesInterceptor代码

public class GetCookiesInterceptor implements Interceptor {
private Context context;
public GetCookiesInterceptor(Context context) {
super();
this.context = context;
}
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
if (!originalResponse.headers("Set-Cookie").isEmpty()) {
final StringBuffer cookieBuffer = new StringBuffer();
Observable.from(originalResponse.headers("Set-Cookie"))
.map(new Func1<String, String>() {
@Override
public String call(String s) {
String[] cookieArray = s.split(";");
return cookieArray[0];
}
})
.subscribe(new Action1<String>() {
@Override
public void call(String cookie) {
cookieBuffer.append(cookie).append(";");
}
});
SharedPreferences sharedPreferences = context.getSharedPreferences("cookie", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("cookie", cookieBuffer.toString());
editor.commit();
}
return originalResponse;
}
}


拦截器部分很简单,但是在做的过程中,因为本人项目中用的是Dagger架构,所以context得引入进来, 在Module中,提供实例

初始化配置

@Provides
@Singleton
public OkHttpClient provideOkHttpClient(Context context,HttpLoggingInterceptor httpLoggingInterceptor, HttpInterceptor httpInterceptor/*, ProgressInterceptor progressInterceptor*/) {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(new GetCookiesInterceptor(context))
.addInterceptor(httpInterceptor)
return client;
}


刚开始在考虑其他几个参数都可以在本类中提供,如http日志

@Provides
@Singleton
public HttpLoggingInterceptor provideHttpLoggingInterceptor() {
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
if (BuildConfig.DEBUG) {
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
} else {
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.NONE);
}
return httpLoggingInterceptor;
}


但是Context如何取提供,后来查阅资料后,发现在AppModule中已经提供了上下文,只需要在写component时候引入进去,会自动调用,测试后发现没问题。

@Module
public class AppModule {
App application;
public AppModule(App application) {
this.application = application;
}
@Provides
@Singleton
public Context provideContext() {
return application;
}
}


cookie的拦截器配置完毕,下面写Api以及调用方法。

Api调用

@POST("data/token/updateToken")
Observable<TestHBC> getHBCToken();


private void getHBCToken() {
SharedPreferences sharedPreferences1 = getSharedPreferences("cookie", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences1.edit();
editor.putString("cookie", "");
editor.commit();
subscription1 = clientApi.getHBCToken()
.compose(SchedulersCompat.<TestHBC>applyIoSchedulers())
.subscribe(new Subscriber<TestHBC>() {
@Override
public void onCompleted() {

}

@Override
public void onError(Throwable e) {
showToast("指数更新失败" + e.toString());
}

@Override
public void onNext(TestHBC testHBC) {
testToken = testHBC.getToken();
SharedPreferences.Editor edit = sharedPreferences.edit();
edit.putString("token", testToken);
edit.commit();

}
});
mCompositeSubscription.add(subscription1);
}


细心的读者会发现,方法的开始地方对sp进行了清空,目的就是避免之前存在内存中的cookie也会默认加到第一次访问的头中,这会导致访问无效,故清空。此处接口返回了token,则通过sp保存,再下次调用接口的时候获取调用即可。

http的拦截器是为了加cookie的头,第一次接口提供的头,你以后得用啊,下面贴http拦截器。

http拦截器

public Response intercept(Chain chain) throws IOException {

SharedPreferences sharedPreferences = context.getSharedPreferences("cookie", Context.MODE_PRIVATE);
String cookie = sharedPreferences.getString("cookie", "");
Request request = chain.request();
Response response;
if (!cookie.equals("")) {
Request compressedRequest = request.newBuilder()
.header("Content-type","application/x-www-form-urlencoded; charset=UTF-8")
.header("cookie", cookie.substring(0,cookie.length()-1))
.build();

response = chain.proceed(compressedRequest);
}else{
response = chain.proceed(request);
}
return response;

}


如果获取的cookie不为空,则表示不是第一次访问接口,加header的cookie,否则不加!

严重提醒:请注意服务器端提供的上传数据类型,笔者在上面吃过亏,用json上传传了很久都没成功,后来发现服务器只支持表单上传,醉了!cookie之所以长度-1,是之前获取的有“;”

第一次接口访问是为了获取cookie和token值,已经完成,都是准备工作,下面才是项目的正常访问。

正常的访问

private void getGoods() {
String token = sharedPreferences.getString("token", "");
/*  UpGetGoods upGetGoods = new UpGetGoods();
upGetGoods.setToken(token);
upGetGoods.setOs("android");*/
subscription2 = clientApi.getGoods("android",token)
.compose(SchedulersCompat.<TestGetGoods>applyIoSchedulers())
.subscribe(new Subscriber<TestGetGoods>() {
@Override
public void onCompleted() {
}

@Override
public void onError(Throwable e) {
showToast("指数更新失败" + e.toString());
}

@Override
public void onNext(TestGetGoods testHBC) {
}
});
mCompositeSubscription.add(subscription1);
}


注释部分即为json上传,吃了大亏咯。getGoods接口如下:

@FormUrlEncoded
@POST("data/store/getGoods")
Observable<TestGetGoods> getGoods(@Field("os") String os,@Field("token") String token);


最后总结一下,其实真正的代码不难,难得是逻辑,得梳理请cookie和token的顺序以及调用关系,啥时获取cookie和token,啥时调用他们,好了,关于retrofit的cookie部分就讲到这里!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息