【问题标题】:Change header on HTTP before retrying an Observable在重试 Observable 之前更改 HTTP 上的标头
【发布时间】:2017-04-10 21:05:13
【问题描述】:

我在前端使用带有 TypeScript 的 Angular 2。我正在尝试实现一个 http 拦截器,它在每个请求上设置授权标头。如果访问令牌过期,我正在尝试重试请求,使用刷新令牌获取新的访问令牌并在重试之前更改当前请求的标头。

如何在retryWhen操作符中更新请求头?

例如这里是HttpInterceptor:

export class HttpInterceptor extends Http {
    get(url: string, options?: RequestOptionsArgs): Observable<Response> {
        return super.get(url, this.setRequestAuthorizationHeader(options)).retryWhen((errors: any) => this.errorHandler(errors));
    }

    private setRequestAuthorizationHeader(options?: RequestOptionsArgs): RequestOptionsArgs {
        // some checks
        // get accessToken from localStorage
        options.headers.append('Authorization', 'Bearer ' + accessToken);
    }

    private errorHandler(errors) {
        return errors.switchMap((err) => {
        if (err.status === 401) {
            let closedSubject = new Subject();

            this.authenticationService.refreshToken()
                .subscribe(data => {
                    // How to update authorization header? This doesn't work.
                    this.defaultOptions.headers.append('Authorization', 'Bearer ' + data.accessToken);

                    closedSubject.next();
                });

            return <any>closedSubject;
        }
        else {
            return Observable.throw(err.json());
        }
    });
}
}

【问题讨论】:

  • 当心使用外部服务刷新令牌(我猜authenticationService 也使用Http),你最终会循环依赖HttpInterceptor 需要authenticationService 需要HttpInterceptor
  • 谢谢,n00dl3。我正在使用注入器动态加载 authenticationService 中的 Http。
  • 可以添加认证服务码吗?
  • 我会使用catch而不是retryWhen,后面的会重播相同的Observable...
  • 如果你有 4 个并行请求,并且它们都有过期的令牌,那么它们都会运行 refreshToken()。正确的?这非常令人困惑,因为 4 个请求将使用相同的令牌生成 4 个 refreshToken。 1 将返回正确的令牌,其他 3 个请求将失败。正确的?我错了吗?解决办法是什么?

标签: angular typescript rxjs observable


【解决方案1】:

我会使用catch 而不是retryWhen,因为后者会重放相同的 observable,并且参数已经设置好了。

顺便说一句,您的主题在errorHanlder 中毫无用处:

export class HttpInterceptor extends Http {
  get(url: string, options ? : RequestOptionsArgs): Observable < Response > {
    return super.get(url, this.setRequestAuthorizationHeader(options)).catch(errors => this.errorHandler(errors, url, options))
  });
}

private setRequestAuthorizationHeader(options ? : RequestOptionsArgs): RequestOptionsArgs {
  // some checks
  // get accessToken from localStorage
  options.headers.append('Authorization', 'Bearer ' + accessToken);
  return options
}

private errorHandler(err: any, url: string, options ? : RequestOptionsArgs) {
  if (err.status === 401) {
    return this.authenticationService.refreshToken()
      .switchMap(data => {
          // save accessToken to localStorage
          return super.get(url, this.setRequestAuthorizationHeader(options));
      });
  }
  return Observable.throw(err.json());
}

还要注意,使用像this.defaultOptions 这样的状态可能不是您的最佳选择,使用 anobservable 会更合适。

【讨论】:

  • 如何在errorHandler 中获得urloptions?如果我使用 url 和带有新 auth 标头的选项发出新请求,我将从失败的请求中丢失订阅者,我希望在请求完成后执行这些订阅者。这就是为什么我尝试使用 retryWhen 来做到这一点。
  • " 如果我​​使用 url 和带有新 auth 标头的选项发出新请求,我将失去我想要在请求完成后执行的订阅者。" ---> 绝对不是。
  • 对于urloption,您可以在参数中传递它们。我编辑了。
  • Okay... 那么errorHandler中返回的observable会和旧的observable结合,新请求的数据会被推送给失败请求的订阅者?我说的对吗?
  • 就是这样,如果达到catch,观察者将从它返回的observable中获取数据。
猜你喜欢
  • 2017-05-31
  • 2014-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-19
  • 2014-02-17
  • 1970-01-01
  • 2015-11-20
相关资源
最近更新 更多