2017-12-27 873 views
1

我發出請求(任何授權,註冊等),然後我發現我需要更新ACCESS-TOKEN,即得到錯誤401如何刷新改進2/rxJava中的ACCESS-TOKEN

這裏是授權請求:

BaseApplication.getApiClient() 
      .signIn(accessToken, body) 
      .subscribeOn(Schedulers.io()) 
      .observeOn(AndroidSchedulers.mainThread()) 
      .subscribe(new SingleObserver<UserProfile>() { 
       @Override 
       public void onSubscribe(Disposable d) { 
        Log.d("-- SignInOnSubscribe", "Subscribed!"); 
       } 

       @Override 
       public void onSuccess(UserProfile userProfile) { 
        if (userProfile.getErrorDetails() != null) { 
         onSignInFinishedCallback.onLoginFailure(userProfile.getErrorDetails()); 
         Log.d("-- SignInOnError", userProfile.getErrorDetails()); 
        } else { 
         onSignInFinishedCallback.onLoginSuccess(userProfile); 
         profileRepository.updateUserProfile(userProfile); 

         Log.d("-- SignInOnSuccess", userProfile.getName()); 
        } 
       } 

       @Override 
       public void onError(Throwable e) { 
        Log.d("-- SignInOnError", e.getMessage()); 

        if (e.getMessage().equals(Constants.CODE_UNAUTHORIZED)){ 
         // Action on error 401 
        } 

        onSignInFinishedCallback.onLoginFailure(e.getMessage()); 
       } 
      }); 

的API請求:

@POST("/api/login") 
Single<UserProfile> getAccessToken(@Body Map<String, String> requestBody); 

@POST("/api/abonent/login") 
Single<UserProfile> signIn(@Header("X-ACCESS-TOKEN") String accessToken, 
          @Body Map<String, String> requestBody); 

例如,該授權要求是request 1,接收TOKENquery 2該請求。

問題:我如何更新TOKEN如果我query 1得到一個錯誤和query 2成功後,回做query 1

回答

0

我不確定您是如何收到新令牌的,因爲返回類型爲getAccessToken()的是Single<UserProfile>。我想這應該是Single<String>代替。也許這是不是這種情況,您會收到令牌在頁眉或作爲UserProfile領域。在這兩種情況下,你可以從下面的解決方案的想法,把它調整到你的情況。

的方法是,我們從原來的一個一個使用令牌存儲裝置,它擁有最先進的最新令牌創建一個新的觀測。我們處理使用composeonErrorResumeNext使令牌刷新請求時,新令牌保存到令牌存儲裝置,和原來的請求被重新嘗試新的令牌這一次的401錯誤。

如需更詳細的說明,請參見下面的代碼中的註釋:

public void signIn(final Map<String, String> body) { 
    Single 
      // Wrap the original request with a "defer" so that the access token is 
      // evaluated each time it is called. This is important because the refreshed 
      // access token should be used the second time around. 
      .defer(new Callable<SingleSource<UserProfile>>() { 
       @Override 
       public SingleSource<UserProfile> call() throws Exception { 
        return BaseApplication.getApiClient() 
          .signIn(accessTokenStore.getAccessToken(), body); 
       } 
      }) 
      // Compose it with a transformer that refreshes the token in the token store and 
      // retries the original request, this time with the refreshed token. 
      .compose(retryOnNotAuthorized(body)) 

      // The code remains the same from here. 
      .subscribeOn(Schedulers.io()) 
      .observeOn(AndroidSchedulers.mainThread()) 
      .subscribe(new SingleObserver<UserProfile>() { 
       @Override 
       public void onSubscribe(Disposable d) { 
        Log.d("-- SignInOnSubscribe", "Subscribed!"); 
       } 

       @Override 
       public void onSuccess(UserProfile userProfile) { 
        if (userProfile.getErrorDetails() != null) { 
         onSignInFinishedCallback.onLoginFailure(userProfile.getErrorDetails()); 
         Log.d("-- SignInOnError", userProfile.getErrorDetails()); 
        } else { 
         onSignInFinishedCallback.onLoginSuccess(userProfile); 
         profileRepository.updateUserProfile(userProfile); 

         Log.d("-- SignInOnSuccess", userProfile.getName()); 
        } 
       } 

       @Override 
       public void onError(Throwable e) { 
        Log.d("-- SignInOnError", e.getMessage()); 

        if (e.getMessage().equals(Constants.CODE_UNAUTHORIZED)) { 
         // Action on error 401 
        } 

        onSignInFinishedCallback.onLoginFailure(e.getMessage()); 
       } 
      }); 
} 

@NonNull 
private SingleTransformer<UserProfile, UserProfile> retryOnNotAuthorized(final Map<String, String> body) { 
    return new SingleTransformer<UserProfile, UserProfile>() { 
     @Override 
     public SingleSource<UserProfile> apply(final Single<UserProfile> upstream) { 
      // We use onErrorResumeNext to continue our Single stream with the token refresh 
      // and the retrial of the request. 
      return upstream.onErrorResumeNext(new Function<Throwable, SingleSource<? extends UserProfile>>() { 
       @Override 
       public SingleSource<UserProfile> apply(Throwable throwable) throws Exception { 
        if (throwable instanceof HttpException 
          && ((HttpException) throwable).code() == 401) { 
         return BaseApplication.getApiClient().getAccessToken(body) 
           // I always use doOnSuccess() for non-Rx side effects, such as caching the token. 
           // I think it's clearer than doing the caching in a map() or flatMap(). 
           .doOnSuccess(new Consumer<String>() { 
            @Override 
            public void accept(String accessToken) throws Exception { 
             // Save the access token to the store for later use. 
             accessTokenStore.storeAccessToken(accessToken); 
            } 
           }) 
           // We don't need the result of getAccessToken() any more, so I 
           // think it's cleaner to convert the stream to a Completable. 
           .toCompletable() 

           // After the token is refreshed and stored, the original request 
           // should be repeated. 
           .andThen(upstream); 
        } 

        // If the error was not 401, pass through the original error 
        return Single.error(throwable); 
       } 
      }); 
     } 
    }; 
} 

更新:令牌存儲裝置僅僅是一個帶有get和儲存方法的常規接口。你應該實現它無論是作爲一個POJO(存儲在字段中的令牌),或者你可以在令牌存儲在共享的偏好,使得令牌生存的應用程序重新啓動。

相關問題