Angular 在重试一个可观察对象之前更改HTTP上的头

Angular 在重试一个可观察对象之前更改HTTP上的头,angular,typescript,rxjs,observable,Angular,Typescript,Rxjs,Observable,我使用的是Angular 2,前端带有TypeScript。我正在尝试实现一个http拦截器,它在每个请求上设置授权头。如果访问令牌过期,我将尝试重试请求,请在重试之前使用刷新令牌获取新的访问令牌并更改当前请求的标头 如何在retryWhen操作符中更新请求标头 例如,这里是HttpInterceptor: export class HttpInterceptor extends Http { get(url: string, options?: RequestOptionsArgs):

我使用的是Angular 2,前端带有TypeScript。我正在尝试实现一个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());
        }
    });
}
}
导出类HttpInterceptor扩展Http{
获取(url:string,options?:RequestOptionsArgs):可观察{
返回super.get(url,this.setRequestAuthorizationHeader(options)).retryWhen((错误:any)=>this.errorHandler(错误));
}
私有setRequestAuthorizationHeader(选项?:RequestOptions参数):RequestOptions参数{
//一些支票
//从本地存储获取accessToken
options.headers.append('Authorization','Bearer'+accessToken);
}
私有errorHandler(错误){
返回错误。开关映射((错误)=>{
如果(错误状态===401){
让closedSubject=新主题();
this.authenticationService.refreshToken()
.订阅(数据=>{
//如何更新授权标头?这不起作用。
this.defaultOptions.headers.append('Authorization','Bearer'+data.accessToken);
closedSubject.next();
});
返回关闭的主题;
}
否则{
返回Observable.throw(err.json());
}
});
}
}

我将使用
catch
而不是
retryWhen
,因为后者重放相同的可观察对象,并且已经设置了参数

顺便说一句,你的主题在
errorhandler
中是无用的:

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());
}
导出类HttpInterceptor扩展Http{
获取(url:string,options?:RequestOptionsArgs):可观察{
返回super.get(url,this.setRequestAuthorizationHeader(options)).catch(errors=>this.errorHandler(errors,url,options))
});
}
私有setRequestAuthorizationHeader(选项?:RequestOptions参数):RequestOptions参数{
//一些支票
//从本地存储获取accessToken
options.headers.append('Authorization','Bearer'+accessToken);
返回选项
}
私有errorHandler(err:any、url:string、options?:RequestOptionsArgs){
如果(错误状态===401){
返回此.authenticationService.refreshToken()
.switchMap(数据=>{
//将accessToken保存到本地存储
返回super.get(url,this.setRequestAuthorizationHeader(选项));
});
}
返回Observable.throw(err.json());
}

还请注意,使用类似于
this.defaultOptions的状态可能不是您的最佳选择,使用anobservable可能更合适。

注意使用外部服务刷新令牌(我猜
authenticationService
也使用Http),您将在需要
HttpInterceptor
认证服务的循环依赖中结束。谢谢,n00dl3。我正在使用注入器动态加载authenticationService中的Http。您可以添加身份验证服务代码吗?我将使用
catch
而不是
retryWhen
,如果您有4个并行请求,并且所有请求都已过期,则后一个请求将重播相同的
Observable
,然后它们都将运行refreshToken()。对吗?这非常令人困惑,因为4个请求将使用相同的令牌生成4个refreshToken。1将返回正确的令牌,其他3个请求将失败。对吗?我错了吗?解决方案是什么?如何在
errorHandler
中获取
url
options
?如果我用url和带有新auth头的选项发出一个新请求,我将失去失败请求的订阅者,我希望在请求完成后执行该请求。这就是我尝试使用retryWhen执行此操作的原因。“如果我使用url和带有新身份验证头的选项发出新请求,我将丢失我希望在请求完成后执行的订阅服务器。”——>绝对不会。对于
url
option
,可以在参数中传递它们。我编辑了。好的。。。因此,errorHandler中返回的可观察对象将与旧的可观察对象相结合,并且来自新请求的数据将被推送到订阅者,从而导致请求失败?我说得对吗?就是这样,如果达到
catch
,观察者将从它返回的可观察数据中获取数据。