You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Angular5中如何在Http拦截器中取消请求并避免重复调用Logout?

Solution to Prevent Multiple Logout Calls on Concurrent 401 Errors

Absolutely, takeUntil is a perfect fit for solving this problem! The core issue right now is that every 401 error triggers the logout logic independently, with no mechanism to block subsequent triggers once the first one starts. Here's how to implement takeUntil (along with a shared signal) to ensure only one logout happens:

Step-by-Step Implementation

  1. Add a termination signal Subject to your interceptor. This will act as a flag to tell subsequent 401 handlers to stop processing.
  2. Guard the logout logic to only run if the signal hasn't been triggered yet.
  3. Use takeUntil to cancel any further error handling for 401s once the logout process starts.

Modified TokenInterceptor Code

import { Injectable, OnDestroy } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { Observable, Subject, throwError } from 'rxjs';
import { takeUntil, catchError } from 'rxjs/operators';
import { AuthGuardService } from './auth-guard.service';
import { Router } from '@angular/router';
import { NotificationService } from './notification.service';

@Injectable()
export class TokenInterceptor implements HttpInterceptor, OnDestroy {
  // Subject to signal that logout has been triggered
  private logoutInProgress$ = new Subject<void>();

  constructor(
    public authService: AuthGuardService,
    private router: Router,
    private notification: NotificationService
  ) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Skip logic for logout request itself to avoid infinite loops
    if (request.url.includes('/logout')) {
      return next.handle(request);
    }

    let authReq = request;
    if (this.authService.tokenValid()) {
      authReq = request.clone({
        headers: request.headers.set("Authorization", 'Bearer ' + this.authService.getToken())
      });
    }

    return next.handle(authReq).pipe(
      catchError((err: any) => {
        if (err instanceof HttpErrorResponse && err.status === 401) {
          // Only proceed if logout hasn't started yet
          if (!this.logoutInProgress$.closed) {
            // Trigger the signal to stop subsequent 401 handlers
            this.logoutInProgress$.next();
            this.logoutInProgress$.complete();
            
            // Run your logout logic once
            this.authService.logout().subscribe({
              complete: () => {
                localStorage.clear();
                this.router.navigate(['login']);
              }
            });
          }
        }
        return throwError(() => err);
      }),
      // Cancel any further processing for this request if logout is in progress
      takeUntil(this.logoutInProgress$)
    );
  }

  ngOnDestroy(): void {
    // Clean up the subject to prevent memory leaks
    this.logoutInProgress$.complete();
  }
}

Key Explanations

  • logoutInProgress$ Subject: This acts as a one-time signal. Once we call next() and complete() on it, all subsequent streams using takeUntil(this.logoutInProgress$) will immediately terminate.
  • Guard Clause: The check !this.logoutInProgress$.closed ensures we only run the logout logic once. Once the subject is completed, this check will fail for all future 401s.
  • takeUntil Operator: This cancels the error handling (and any ongoing request processing) for any requests that hit a 401 after the logout process has started, preventing duplicate logout calls.

This approach ensures that no matter how many concurrent 401 responses you receive, your logout API is only called once, and all subsequent 401 errors are ignored.

内容的提问来源于stack exchange,提问作者Rahul K R

火山引擎 最新活动