2017-12-27 876 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(存储在字段中的令牌),或者你可以在令牌存储在共享的偏好,使得令牌生存的应用程序重新启动。

相关问题