You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 8中next.handle重发请求失效问题求助

Token过期返回401后刷新Token但未自动重发请求的问题

问题描述

Token过期返回401状态码后,获取刷新Token的API已触发,但内部的next.handle并未重发原请求,期望调用refreshToken API后自动重发role API请求。

当前代码

export class InterceptService implements HttpInterceptor {

    isRefreshingToken = false;
    isUserLoggedIn$: Observable<boolean> = this.store.pipe(select(isLoggedIn));

    constructor(public auth: AuthService, private store: Store<AppState>,http: HttpClient) {}

    private addTokenHeader(request: HttpRequest<any>, token: string) {
        request = request.clone({
            setHeaders: {
                Authorization: `Bearer ${token})}`
            }
        });
        return request;
    }

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

        request = request.clone({
            setHeaders: {
                Authorization: `Bearer ${localStorage.getItem(environment.authTokenKey)}`
            }
        });

        return next.handle(request).pipe(
            tap(
                event => {
                     if (event instanceof HttpResponse) {
                    }
                },
                error => {
                    if(error.status === 401 && !this.isRefreshingToken && this.isUserLoggedIn$) {
                        this.isRefreshingToken = true;
                        this.auth.getRefreshToken().subscribe( (authToken: IAuthToken) =>
                            {
                                this.store.dispatch(new TokenSet({authToken}));
                                let newReq = this.addTokenHeader(request, authToken.token);
                                return next.handle(newReq);
                            }
                            
                        );  
                            
                    }
                    else {
                        this.isRefreshingToken = false;
                    }
                }
            )
        );
    }
}

当前现象

请求流程截图

解决方案

问题核心在于tap操作符仅用于副作用处理,不会修改或替换原Observable流,内部订阅getRefreshToken后返回的next.handle(newReq)并未被加入到原响应流中,因此重发请求不会被触发。同时代码还存在两个细节问题:addTokenHeader里的Authorization头多了一个右括号,以及直接判断Observable类型的isUserLoggedIn$不会得到实际的登录状态。

修改后的完整代码:

export class InterceptService implements HttpInterceptor {

    isRefreshingToken = false;
    isUserLoggedIn$: Observable<boolean> = this.store.pipe(select(isLoggedIn));

    constructor(public auth: AuthService, private store: Store<AppState>, private http: HttpClient) {}

    private addTokenHeader(request: HttpRequest<any>, token: string) {
        return request.clone({
            setHeaders: {
                Authorization: `Bearer ${token}` // 修复多余的右括号
            }
        });
    }

    intercept(
        request: HttpRequest<any>,
        next: HttpHandler
    ): Observable<HttpEvent<any>> {
        // 给请求添加Token头
        const token = localStorage.getItem(environment.authTokenKey);
        if (token) {
            request = this.addTokenHeader(request, token);
        }

        return next.handle(request).pipe(
            catchError(error => {
                // 仅处理401、未在刷新Token、且用户处于登录状态的情况
                if (error.status === 401 && !this.isRefreshingToken) {
                    return this.isUserLoggedIn$.pipe(
                        switchMap(isLoggedIn => {
                            if (!isLoggedIn) {
                                // 用户未登录,直接抛出错误
                                return throwError(() => error);
                            }
                            this.isRefreshingToken = true;
                            // 刷新Token
                            return this.auth.getRefreshToken().pipe(
                                switchMap((authToken: IAuthToken) => {
                                    this.store.dispatch(new TokenSet({ authToken }));
                                    localStorage.setItem(environment.authTokenKey, authToken.token);
                                    // 重发原请求
                                    const newReq = this.addTokenHeader(request, authToken.token);
                                    return next.handle(newReq);
                                }),
                                // 无论刷新成功或失败,重置刷新状态
                                finalize(() => {
                                    this.isRefreshingToken = false;
                                })
                            );
                        })
                    );
                }
                // 其他错误直接抛出
                return throwError(() => error);
            })
        );
    }
}

关键修改点

  1. 替换tap为catchError:捕获401错误后返回新的Observable流,确保重发请求能被订阅处理
  2. 正确处理isUserLoggedIn$:通过switchMap订阅登录状态Observable,获取实际的布尔值
  3. 修复Authorization头语法错误:移除多余的右括号
  4. 添加finalize重置状态:无论刷新Token成功或失败,都重置isRefreshingToken,避免后续请求被阻塞
  5. 更新本地存储Token:刷新Token后同步更新localStorage中的Token,确保后续请求使用新Token

内容的提问来源于stack exchange,提问作者Asim Sapkota

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 01:31:27