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

Angular HTTP拦截器调用服务时失效问题求助

Angular HTTP拦截器调用AuthService获取Token时无后续逻辑问题

我创建了HTTP拦截器,用来自动给所有HTTP请求添加请求头,无需手动配置每个请求。但在拦截器内调用GoogleAuthService获取token时,拦截器仅打印初始日志Interceptor: Intercepting request...,无法进入服务调用后的逻辑;而硬编码token时,拦截器可正常工作。

拦截器文件

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable, from } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { GoogleAuthService } from './services/auth/auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private authService: GoogleAuthService) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    console.log('Interceptor: Intercepting request...');
    
    return this.authService.requestToken().pipe(
      switchMap(token => {
        console.log('Interceptor: Token obtained -', token);
        const authRequest = this.addAuthenticationToken(request, token);
        return next.handle(authRequest);
      })
    );
  }

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

服务文件

该函数调用GCP云函数获取token,供拦截器使用:

import { Injectable, SkipSelf } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, catchError, throwError } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class GoogleAuthService {
  audience = 'https://cloud.run.app';

  constructor(private http: HttpClient) {}

  requestToken(): Observable<any> {
    const requestUrl = "https://us-central1-gcp.cloudfunctions.net/get-token"
    const tokenUrl = `${requestUrl}?url=${this.audience}`
    return this.http.get(tokenUrl)
  }
}

模块文件

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { GoogleAuthService } from './services/auth/auth.service';
import { AuthInterceptor } from './auth.interceptor';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true,
      deps: [GoogleAuthService]
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

浏览器请求状态截图

浏览器请求阻塞截图


问题根源

这是循环依赖死锁导致的:

  1. 拦截器依赖GoogleAuthService,而GoogleAuthService又依赖HttpClient
  2. HttpClient初始化时会触发所有HTTP_INTERCEPTORS的初始化,进而回到拦截器,形成闭环,导致requestToken()的调用永远无法完成

解决方案

方案1:标记并跳过Token请求的拦截

修改GoogleAuthService的requestToken()方法,添加自定义请求头,让拦截器跳过该请求:

requestToken(): Observable<any> {
  const requestUrl = "https://us-central1-gcp.cloudfunctions.net/get-token"
  const tokenUrl = `${requestUrl}?url=${this.audience}`
  // 添加自定义头,告知拦截器跳过当前请求
  return this.http.get(tokenUrl, {
    headers: new HttpHeaders({ 'skipInterceptor': 'true' })
  })
}

然后在拦截器的intercept方法开头增加判断逻辑:

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  // 跳过获取Token的请求,避免循环依赖
  if (request.headers.has('skipInterceptor')) {
    // 删除自定义头后继续请求,避免传递到后端
    return next.handle(request.clone({ headers: request.headers.delete('skipInterceptor') }));
  }

  console.log('Interceptor: Intercepting request...');
  
  return this.authService.requestToken().pipe(
    switchMap(token => {
      console.log('Interceptor: Token obtained -', token);
      const authRequest = this.addAuthenticationToken(request, token);
      return next.handle(authRequest);
    })
  );
}

方案2:直接使用HttpClientBackend绕开拦截器

在GoogleAuthService中注入HttpClientBackend,直接发起请求,完全绕开拦截器链:

import { Injectable } from '@angular/core';
import { HttpClientBackend, HttpRequest, HttpResponse } from '@angular/common/http';
import { Observable, catchError, throwError } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class GoogleAuthService {
  audience = 'https://cloud.run.app';

  constructor(private httpBackend: HttpClientBackend) {}

  requestToken(): Observable<any> {
    const requestUrl = "https://us-central1-gcp.cloudfunctions.net/get-token"
    const tokenUrl = `${requestUrl}?url=${this.audience}`
    
    const request = new HttpRequest('GET', tokenUrl);
    return new Observable(observer => {
      this.httpBackend.handle(request).subscribe({
        next: (event) => {
          if (event instanceof HttpResponse) {
            observer.next(event.body);
            observer.complete();
          }
        },
        error: (err) => {
          observer.error(err);
        }
      });
    }).pipe(catchError(err => throwError(() => err)));
  }
}

额外优化建议

给requestToken()添加错误处理,避免Token请求失败导致所有业务请求阻塞:

requestToken(): Observable<any> {
  const requestUrl = "https://us-central1-gcp.cloudfunctions.net/get-token"
  const tokenUrl = `${requestUrl}?url=${this.audience}`
  return this.http.get(tokenUrl, {
    headers: new HttpHeaders({ 'skipInterceptor': 'true' })
  }).pipe(
    catchError(err => {
      console.error('获取Token失败:', err);
      return throwError(() => err);
    })
  );
}

内容的提问来源于stack exchange,提问作者Umamaheswararao Meka

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 18:45:08