Retrofit——Cookie

今天学习了有关http的cookie知识后,刚好想结合retrofit来试验一下学习成果,于是拿了别人的接口来做实验。

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

  • fiddler抓包
    用工具抓包,获取接口信息,大概信息如下:
    http://*****/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部分就讲到这里!

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值