Angular 17拦截器引发无限循环问题求助
解决Angular 17拦截器无限循环问题
问题根源
无限循环的核心触发点是路由导航与拦截器的相互触发:
- 当401/403错误或令牌过期时,调用
router.navigateByUrl('/signin'),若登录页涉及HTTP请求(如懒加载模块、静态资源),会再次触发拦截器 - 原代码仅在auth类接口时检查令牌过期,其他接口直接添加过期令牌,导致反复触发401错误,进而重复执行登录导航
- 同步执行导航操作会打乱HTTP请求流,加剧循环问题
修复方案
1. 跳过静态资源与路由页面请求
拦截器排除Angular内部的页面资源请求(如HTML、JS、CSS文件),避免导航触发拦截器重复执行。
2. 统一令牌过期校验逻辑
无论请求类型,只要存在令牌就先校验是否过期,避免过期令牌被反复添加到请求头。
3. 异步执行导航并添加防重复判断
用setTimeout异步执行导航,同时检查当前路由是否已在登录页,防止重复导航。
修改后的拦截器代码
import { inject } from '@angular/core'; import { Router } from '@angular/router'; import { catchError, tap } from 'rxjs/operators'; import { throwError } from 'rxjs'; import { LoaderService } from '../Services/loader/loader.service'; import { jwtDecode } from 'jwt-decode'; import { HttpEventType, HttpErrorResponse } from '@angular/common/http'; export const httpInterceptor: HttpInterceptorFn = (req: HttpRequest<any>, next: HttpHandlerFn) => { const router = inject(Router); const loader = inject(LoaderService); // 跳过静态资源和路由页面请求,避免导航触发拦截器循环 if (req.url.match(/\.(html|js|css|png|jpg)$/) || req.url.includes('signin')) { return next(req); } let token: string | null = null; if (typeof localStorage !== 'undefined') { token = localStorage.getItem('authToken'); } // 统一校验令牌是否过期 let isTokenExpired = false; if (token) { const decodedToken = jwtDecode(token); isTokenExpired = decodedToken && decodedToken.exp ? decodedToken.exp < Date.now() / 1000 : false; if (isTokenExpired) { localStorage.removeItem('authToken'); // 异步执行导航,避免同步打断请求流 setTimeout(() => router.navigateByUrl('/signin')); return throwError(() => 'Token expired'); } } // 给非auth接口添加有效令牌 if (token && !isTokenExpired && !req.url.includes('auth/')) { req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); } // 加载状态处理 loader.setLoading(true, req.url); return next(req).pipe( tap(event => { if (event.type === HttpEventType.Response) { loader.setLoading(false, req.url); } }), catchError((error: HttpErrorResponse) => { loader.setLoading(false, req.url); if ([401, 403].includes(error.status)) { localStorage.removeItem('authToken'); // 检查当前路由,避免重复导航 if (!router.url.includes('/signin')) { setTimeout(() => router.navigateByUrl('/signin')); } } return throwError(() => error); }) ); };
额外优化建议
- 给
HomeComponent的courses数组添加明确类型(如定义Course接口),避免使用无类型数组 - 在
LoaderService中添加防抖逻辑,防止频繁调用setLoading导致的状态异常 - 可新增全局导航标志位,进一步避免重复触发登录跳转
内容的提问来源于stack exchange,提问作者Ilyoskhuja
相关产品推荐
相关产品推荐

