-1

我的问题如下:我的 web 应用程序使用了可以过期的身份验证令牌。当它们即将到期时,服务器会发送 401 错误,提示浏览器首先执行令牌刷新请求。我正在使用retryWhen()操作员来处理这种情况。操作员使用以下retryWhen()代码请求新令牌。

  public tokenExpired = ({
    maxRetryAttempts = 1
  }: {
    maxRetryAttempts?: number,
  } = {}) => (attempts: Observable<any>) => {
    return attempts.pipe(
      mergeMap((error, i) => {
        const retryAttempt = i + 1;
        if((error && error.headers && (!error.headers.get("reason-unauthorized") || !(error.headers.get("reason-unauthorized") === "authentication-token-expires-soon"))) || retryAttempt > maxRetryAttempts) {
          return throwError(error);
        }
          return this.requestNewToken().pipe(
            concatMap(refreshStatus => {
              if(refreshStatus === TokenRefreshStatus.TOKEN_REFRESHED) 
                return timer(0);
              else if(refreshStatus === TokenRefreshStatus.TOKEN_REFRESH_FAILED) 
                return throwError(error);
              else if(refreshStatus === TokenRefreshStatus.AWAIT_REFRESH && this.loggedIn.value){
                let maxWaitTimeInMilliSeconds = 4000; 
                interval(100).pipe(takeWhile(() => !(this.tokenRefreshStatus !== TokenRefreshStatus.AWAIT_REFRESH || maxWaitTimeInMilliSeconds <=0)))
                  .subscribe(value => {maxWaitTimeInMilliSeconds -= 100;});
                if(this.tokenRefreshStatus === TokenRefreshStatus.TOKEN_REFRESHED)
                  return timer(0); 
                else return throwError(error);
                }
              else 
                return throwError(error); 
            }));
      })
    );
  }

所以 retryWhen 操作符将tokenExpired作为它的 lambda retryWhen(tokenExpired())。因为几乎可以同时触发多个请求,所以我以这样一种方式编写了代码,即只有第一个请求会触发令牌刷新 http 请求,而通过this.requestNewToken()类似调用的任何其他请求都会从该方法retryWhen获得AWAIT_REFRESH消息。requestNewToken()这样我可以防止对新令牌的多次请求。

当接收到 AWAIT_REFRESH 时,代码必须每 100 毫秒检查一次是否tokenRefreshStatus已更改为TOKEN_REFRESHEDTOKEN_REFRESH_FAILED。它现在可以重复这个更长的时间,然后大约 4000 毫秒。maxWaitTimeInMilliSeconds因此,当达到 0 或更低时,间隔也必须停止重复。之后有一个 if 语句检查是否TokenRefreshStatus等于TOKEN_REFRESHED,如果是,则返回,timer(0)如果不是,则抛出retryWhen操作员收到的原始错误。

我的问题是应用程序不等待间隔代码完成。它只是直接进入 if 语句,导致它总是抛出错误。我认为我不应该订阅间隔,而是应该将其作为一个新的可观察对象返回,该可观察对象执行间隔迭代,然后检查间隔完成后令牌是否已刷新。我只是不知道我该怎么做。

谁能指出我正确的方向?谢谢

4

1 回答 1

0

我的问题是应用程序不等待间隔代码完成。

发生这种情况是因为间隔创建了一个异步的可观察对象。我会尝试让您的 refreshStatus 函数异步调用 await 当您希望它等待时(即:将您的时间间隔包装在等待它的承诺中):

concatMap(async refreshStatus => {
     .
     .
     .

     function checkit(){
       return new Promise((res, rej)=>{

        interval(100).pipe(takeWhile(() =>{
          if(!(this.tokenRefreshStatus !== TokenRefreshStatus.AWAIT_REFRESH || maxWaitTimeInMilliSeconds <=0)){
            return true;
          }
          else{
            res(true);
            return false;
          }
        }))
        .subscribe(value => {maxWaitTimeInMilliSeconds -= 100;});
      });
    }
    await checkit();

     .
     .
     .
})

我希望这可以帮助您解决问题。

于 2020-06-19T01:05:56.460 回答