【问题标题】:Angular - RxJS - Http retry in case of authentication errorAngular - RxJS - 发生身份验证错误时的 Http 重试
【发布时间】:2017-08-04 05:50:33
【问题描述】:

我是使用 Angular 和 Rxjs 的新手。

基本上,应用使用 Api 并需要提供有效的身份验证令牌。 这个令牌是短暂的,如果它已经过期,它可以被刷新。

我编写了一个特定的 http 服务,它为每个请求添加正确的标头,并且在身份验证失败的情况下尝试刷新令牌并重试。

以下是我的实现方式:

export class HttpAService extends Http {

  constructor(
    backEnd: XHRBackend,
    options: RequestOptions,
    private authenticationService: AuthenticationService
  ) {
    super(backEnd, options);
    // get an authentication token from an authentication service
    let token = authenticationService.token;
    options.headers.set('Authorization', `Bearer ${token}`);
  }

  private request1(
    url: string | Request,
    options?: RequestOptionsArgs
  ): Observable<Response> {
    // get a token from an authentication service
    let token = this.authenticationService.token;
    // add it to the request headers
    if (typeof url === 'string') {
      if (!options) {
        options = { headers: new Headers };
      }
      options.headers.set('Authorization', `Bearer ${token}`);
    } else {
      url.headers.set('Authorization', `Bearer ${token}`);
    }
    // make the request
    return super.request(url, options);
  }

  public request(
    url: string | Request,
    options?: RequestOptionsArgs
  ): Observable<Response> {
    // init retry flag
    let retried: boolean = false;
    // make the request and retry one time in case of failure
    const obs = Observable.defer(() => this.request1(url, options));
    return obs.retryWhen(attempt => {
      // request failed
      return attempt.flatMap(res => {
        // if it's the first round and an authentication error try to refresh token
        if ((!retried) && (500 === res.status) && ('Unauthenticated.' == res.json().message)) {
          retried = true;
          // refresh attempts to create a new token that can be retrieved using authenticationService.getToken.
          return this.authenticationService.refresh();
        }
        return Observable.throw(res);
      })
    })
  }
}

我遇到的问题是让重试请求使用新令牌(换句话说,在重试期间让 request1 函数重新运行)。看来 defer(...) 成功了。

我想知道这个实现是否正确,是否有更优雅的方式来实现这个行为。

感谢您的回复。

【问题讨论】:

    标签: angular http rxjs


    【解决方案1】:

    对于那些遇到这个问题的人,我认为推荐的运算符是retryWhen。这是一个例子。

    get(url:string): Observable<Object> {
      return this.httpClient.get(url, {headers: this.authHeaders}).pipe(
        retryWhen(errors => errors.pipe(
          switchMap((e:HttpErrorResponse) => {
            if (e.status === 403) return this.refreshAuthToken();
            console.warn("HTTP GET error", url, e); //like 500 internal server error
            throw e;
          })
        ))
      )
    }
    

    但是有一个缺陷。虽然refreshAuthToken 完美运行,但原始 HTTP 可观察对象仍然卡在未经过身份验证的令牌上。不知道如何解决这个问题。

    所以现在我使用一个可行的解决方案,但它递归地调用我的get 方法而不是使用运算符。

    get(url:string): Observable<Object> {
      return this.httpClient.get(url, {headers: this.authHeaders}).pipe(
        catchError((e:HttpErrorResponse) => {
          if (e.status === 403) {
            return this.refreshAuthToken().pipe(
              switchMap(success => success? this.get(url) : of(null)) //recursive call not cool :(
            )
          }
          console.warn("HTTP GET error", url, e); //like 500 internal server error
          return of(null);
        })
      )
    }
    

    希望有人提供更好的答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-15
      • 1970-01-01
      • 2020-05-16
      • 2019-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多