網(wǎng)絡(luò)請求時(shí)關(guān)于cookie或token失效的解決方案

當(dāng)一次網(wǎng)絡(luò)請求(比如說請求購物車的數(shù)據(jù),這時(shí)是需要驗(yàn)證用戶身份的標(biāo)識的,例如cookie或者token)
想到的三種方法:

1.最開始沒用rxjava之前就是用的這種,但是感覺實(shí)在累贅。當(dāng)token失效后重新請求登錄接口,當(dāng)?shù)卿洺晒笸ㄖ鹊腁ctivity重新加載數(shù)據(jù)。這樣需要對每個(gè)接口都進(jìn)行token是否失效的判斷。

2.使用Intercept(參考這篇文章,但是Okhttpclien3.0刪除了ErrorHandler)onErrorResumeNext操作符實(shí)現(xiàn)app與服務(wù)器間token機(jī)制
http://blog.csdn.net/johnny901114/article/details/51533586
在intercept方法中拿到返回的json字符串,然后判斷token是否失效,如果失效,那么重新登錄,但是這兒需要注意的是因?yàn)樾枰^續(xù)往下傳遞請求,登錄接口的請求必須是同步的?。╬s:后來朋友想了另一個(gè)辦法,在intercept中拋出異常,這兒就需要用到第三種方法了)

3.使用retryWhen操作符
(關(guān)于retryWhen這篇博客講的非常好http://m.itdecent.cn/p/023a5f60e6d0
最開始我的理解有問題。我的代碼是這樣的。

ApiClient.getInstance().getLiveApi().getFollow(cookie)
                .flatMap(new Func1<FollowLive, Observable<FollowLive>>() {
                    @Override
                    public Observable<FollowLive > call(FollowLive followLive) {
                        if (myCourse.status == 205) {
                            return Observable.error(new Exception("kkkk"));
                        }
                        return Observable.just(followLive);
                    }
                })
                .retryWhen(new RetryWithDelay(3, 1000))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<FollowLive >() {
                    @Override
                    public void call(FollowLive response) {
                        fillData(response);
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        Log.i("===========k", throwable.toString());
                    }
                });

然后我在retryWhen中進(jìn)行了重新登錄獲取到了最新的cookie,結(jié)果顯示沒有獲取到正確的數(shù)據(jù),我猜,難道retryWhen只重試了flatMap?當(dāng)然不是,我抓包得到的結(jié)果是,重新進(jìn)行了網(wǎng)絡(luò)請求,但是并沒有使用新的cookie,為什么呢,cookies作為一個(gè)成員變量,他的值變化了??!
然后我寫了個(gè)just的例子測試了下!

        str = "aaa";
        Observable.just(str).map(new Func1<String, String>() {
            @Override
            public String call(String s) {
                Log.i("====", "s == " + s);
                if ("aaa".equals(s)) throw new RuntimeException(s);
                return s + "123";
            }
        }).retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
            @Override
            public Observable<?> call(Observable<? extends Throwable> observable) {
                return observable.zipWith(Observable.range(1, 4), new Func2<Throwable, Integer, Integer>() {
                    @Override
                    public Integer call(Throwable throwable, Integer i) {
                        str = "ggg";
                        return i;
                    }
                }).flatMap(new Func1<Integer, Observable<? extends Long>>() {
                    @Override
                    public Observable<? extends Long> call(Integer retryCount) {
                        return Observable.timer(1, TimeUnit.SECONDS);
                    }
                });
            }
        }).subscribe(new Action1<String>() {
            @Override
            public void call(String s) {
                Log.i("====k", "s = " + s);
            }
        }, new Action1<Throwable>() {
            @Override
            public void call(Throwable throwable) {
                Log.i("====", "throwable = " + throwable.getMessage());
            }
        });

結(jié)果是

aaa                                      
aaa                             
aaa                         
...    

what?
為啥???為什么后面不打印ggg呢?
看這里吧。
關(guān)于retryWhen的issue
https://github.com/ReactiveX/RxJava/issues/4840
也就說retryWhen每次重試的都是Source Observable!而Observable.just(str)已經(jīng)創(chuàng)建完成,每次傳遞給map操作符的都是創(chuàng)建時(shí)候用的那個(gè)str,網(wǎng)絡(luò)請求的那個(gè)類似,當(dāng)下次重試的時(shí)候使用的是已經(jīng)創(chuàng)建好的Observable(而這個(gè)Observable創(chuàng)建的時(shí)候使用的是空的cookie)為了保證使用最新的cookie,使用defer操作符,原理類似于fromCallable.
當(dāng)然也可以這樣寫:

 Observable.just(null).flatMap(new Func1<Object, Observable<FollowLive>>() {
            @Override
            public Observable<FollowLive> call(Object o) {
                return ApiClient.getInstance().getLiveApi().getFollow(cookie);
            }
        })

再來說第二條提到的那個(gè)拋出異常的方法。

具體實(shí)現(xiàn)就是Intercept和RetryWhen結(jié)合,在Intercept中進(jìn)行token是否失效的判斷,如果token失效那么就直接拋出異常,然后在retryWhen中進(jìn)行重新登錄,并給token設(shè)置最新的值。這樣就避免了同步請求的問題。不過需要注意:

1.因?yàn)橹匦碌卿浭钱惒秸埱?,所以需要對retryWhen中的重試進(jìn)行限制,即重新請求原先接口需要延遲(用timer操作符)

2.對重新登錄次數(shù)進(jìn)行限制
3.最好自定義拋出的異常,這樣方便在Subscriber的onError方法或者retryWhen中進(jìn)行判斷是否是token失效,萬一后續(xù)還有其他問題需要在Intercept中處理呢。

4.最大的弊端是對所有的接口都進(jìn)行了token是否失效的判斷(因?yàn)镮ntercept會是全局的),所以在Intercept中對token是否失效那兒的判斷可以自行處理,比如說用戶是否登錄?這樣的判斷。

2017.1.16更新:使用BlockingObservable阻塞操作符可以避免了這種狀況:
比如說登錄接口返回較慢(慢的超過了retryCount*retryDelayMillis),那么會一直重試登錄接口和follow數(shù)據(jù)接口,直到超過重試次數(shù),如果這時(shí)候仍然沒有獲取到數(shù)據(jù),才會拋出異常,具體的看代碼。

其他參考:https://lorentzos.com/improving-ux-with-rxjava-4440a13b157f#.e4b4absxs
pps:這是我現(xiàn)在使用的一套方法,有什么不對的地方希望大家可以留言改進(jìn),謝謝!
參考代碼:https://github.com/xturbofan/TokenDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容