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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:57:52