【问题标题】:Angular HttpInterceptor: How to use RxJS for multiple conditionsAngular HttpInterceptor:如何在多个条件下使用 RxJS
【发布时间】:2019-03-21 18:31:49
【问题描述】:

这里是AuthInterceptor

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

    constructor(private authService: AuthService) { }

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

        const Token = this.authService.getToken();

        if (!Token) {
            return next.handle(req);
        }
 
        // Refresh Token first
        if (Token.expiresRefreshToken && Number(Token.expiresRefreshToken) < Date.now()) {
            this.authService.refreshTokenRefresh(Token.tokenref)
            .subscribe((response) => {
                localStorage.setItem('tokenref', response.tokenref);
                localStorage.setItem('tokenrefexp', response.tokenrefexp);
            });
        }
        // Then next Access Token
        if (Token.expiresToken && Number(Token.expiresToken) < Date.now()) {
            this.authService.refreshToken(Token.tokenref)
            .subscribe((response) => {
                localStorage.setItem('token', response.token);
                localStorage.setItem('tokenexp', response.tokenexp);
            });
        }
        // Original request with updated custom headers
        return next.handle(req.clone({
            headers: req.headers
            .set('Authorization', 'Bearer ' + localStorage.getItem('token'))
            .set('X-Auth-Provider', localStorage.getItem('provider'))
        }));
    }

}

我需要在发送请求之前评估这些条件,因为在方法 refreshTokenrefreshTokenRefresh 之后某些自定义标头可能会发生变化。有没有办法评估 RxJS 运算符中的所有内容?第一个条件 (refreshTokenRefresh),然后是第二个条件 (refreshToken),最后是 req

更新:我收到此错误:RangeError: Maximum call stack size exceeded. 如何解决此问题?

【问题讨论】:

标签: javascript angular typescript rxjs angular-http-interceptors


【解决方案1】:

我们希望等到某些请求完成(评估顺序无关紧要?)而不是另一个请求。

const queue = this.handleRefreshToke(this.handleRefreshTokenRefresh([])); - 在我们调用next.handle 之前将所有应完成的请求放在此处。 使用forkJoin 等待所有请求(放入队列)完成,然后映射到另一个 Obervable (mergeMap)。

PS 我们还可以将handleRefreshTokenRefreshhandleRefreshToke 移动到单独的HttpInterceptor。

已编辑为了防止拦截器的递归调用,我们应该 skip interceptors 进行 refreshTokens 调用。

export const InterceptorSkipHeader = 'X-Skip-Interceptor';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

    constructor(private authService: AuthService) { }

    handleRefreshTokenRefresh(queue: Observable<void>[]) {
        const Token = this.authService.getToken();
        if (Token.expiresRefreshToken && 
            const req = this.authService.refreshTokenRefresh(Token.tokenref)
              .pipe(tap((response) => {
                localStorage.setItem('tokenref', response.tokenref);
                localStorage.setItem('tokenrefexp', response.tokenrefexp);
            }));
            return [...queue, req];
        }
        return queue;
    }

    handleRefreshToke(queue: Observable<void>[]) {
        const Token = this.authService.getToken();
        if (Token.expiresToken && Number(Token.expiresToken) < Date.now()) {
            const req = this.authService.refreshToken(Token.tokenref)
              .subscribe((response) => {
                localStorage.setItem('token', response.token);
                localStorage.setItem('tokenexp', response.tokenexp);
            });
            return [...queue, req];
        }
        return queue;
    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
       if (req.headers.has(InterceptorSkipHeader)) {
            const headers = req.headers.delete(InterceptorSkipHeader);
            return next.handle(req.clone({ headers }));
        }

        const Token = this.authService.getToken();

        if (!Token) {
            return next.handle(req);
        }

        const queue = this.handleRefreshToke(this.handleRefreshTokenRefresh([]));

        return forkJoin(queue).pipe(
            mergeMap(()=>{
                return next.handle(req.clone({
                    headers: req.headers
                        .set('Authorization', 'Bearer ' + localStorage.getItem('token'))
                        .set('X-Auth-Provider', localStorage.getItem('provider')),
                }));
            })
        );
    }

}

InterceptorSkipHeader 添加到 refreshTokens 以跳过拦截器。

// AuthService

refreshTokenRefresh(token){
   const headers = new HttpHeaders().set(InterceptorSkipHeader, '');
   return this.httpClient
    .get(someUrl, { headers })
}

refreshToken(token){
   const headers = new HttpHeaders().set(InterceptorSkipHeader, '');
   return this.httpClient
    .get(someUrl, { headers })
}

【讨论】:

  • 评估订单确实很重要,因为我需要一个有效的刷新令牌(第一个条件)来生成访问令牌(第二个条件)。
  • 你可以用concat代替forkJoin
  • 你能验证我的回答吗?我认为可能有问题,因为我的服务器有点不稳定:使用拦截器时多次重启。我在控制台 RangeError: Maximum call stack size exceeded 上收到此错误。
  • 对于handleRefreshToken handleRefreshTokenRefresh ,您需要跳过interceptor
  • 谢谢!我之前尝试过停止使用HttpBackEnd 调用拦截器,但没有成功。
猜你喜欢
  • 2019-03-17
  • 2017-08-18
  • 2020-02-17
  • 1970-01-01
  • 2018-07-18
  • 2021-01-04
  • 2018-08-12
  • 2021-08-03
  • 2017-10-06
相关资源
最近更新 更多