【问题标题】:Use RxJs to create HttpRequest queue in Angular2+ interceptors使用 RxJs 在 Angular2+ 拦截器中创建 HttpRequest 队列
【发布时间】:2018-07-21 03:21:23
【问题描述】:

我在 Angular5 中使用拦截器为每个请求添加一个 Authorization 标头。但是,来自后端的令牌可能会过期,在这种情况下,必须进行令牌刷新。如果多个HttpRequest<any>同时进来,我没办法让它们排队,所以会发出多个未经授权的请求,服务器会返回多个401响应,直到最后一个令牌刷新完成,剩余的请求可以重试。目前,每个请求大约需要 3 次失败尝试,直到其中一个最终成功。当前实现:

@Injectable()
export class TokenInterceptor implements HttpInterceptor {

    private refreshInProgress: boolean = false;

    constructor(private injector: Injector, private router: Router) {}

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

        request = request.clone({
            setHeaders: {
                Authorization: `${this.injector.get(AuthService).getAccessToken()}`
            }
        });

        return next.handle(request).catch((error: any) => {
            if(error instanceof HttpErrorResponse) {
                switch(error.status) {
                    case 401:
                        if(!this.refreshInProgress) {
                            this.refreshInProgress = true;
                            this.injector.get(AuthService).getRefreshToken().subscribe(
                                (success: boolean) => {
                                    if(!success) {
                                        this.router.navigate(['/signin']);
                                    }
                                }
                            );
                            return this.intercept(request, next);
                        } else {
                            return this.intercept(request, next).delay(700);
                        }
                    default:
                        return Observable.throw(error);
                }
            }
            return Observable.throw(error);
        })
        .finally(() => {
            this.refreshInProgress = false;
        });
    }
}

如您所见,我所知道的唯一选择是添加 refreshInProgress 布尔值以尝试阻止其他请求立即发出,而是尝试让它们等待 700 毫秒后再重试。这不是一个很好的解决方案,但它可以通过。

我想我想做的是创建一个unauthorizedRequestQueue 或者一个BehaviorSubject&lt;HttpRequest&lt;any&gt;&gt;,然后如果一个401 已经出去,让后续的HttpRequests 排队,直到第一个调用返回刷新令牌。但是,我对 RxJs 库仍然很陌生,并且无法弄清楚如何做这样的事情。

我希望 SO 社区能够将我推向正确的方向,让我了解如何实现此类功能。或者也许还有其他更好的方法来做我想做的事情?

【问题讨论】:

    标签: angular rxjs observable interceptor subject


    【解决方案1】:

    您可以使用如下所示的同步简单模式,还可以指定要重试多少次。在这个用例中不需要拦截器

    header={'Authorization':...}
    auth() {
      // catch the unauth error and switch to refresh token 
      return this.http.get(authUrl).catch((response: Response) => { 
         return this.http.get(refreshTokenUrl)
                // direct to refresh token call 
               .do(newToken=>this.header['Authorization']=newToken)
               // throw so it can repeat the auth process
               .mapTo(Observable.throw('not yet done')
      }).retry()
     }
    

    【讨论】:

    • 用户的初始登录令牌通常不会引发异常,并且我只会在给定调用返回401 时知道令牌已超时。由于拦截器在每次调用时都添加了 auth 令牌,它是 401 将首先报告给应用程序的地方,所以我仍然需要处理那里的错误。我会看看我是否可以以某种方式合并.retry() 方法。
    • 遇到401可以故意扔掉,这样会路由到catch块。 Observable.throw('401')
    猜你喜欢
    • 2016-06-28
    • 2016-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-24
    • 2019-08-21
    • 2023-03-19
    • 1970-01-01
    相关资源
    最近更新 更多