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
- Add a termination signal Subject to your interceptor. This will act as a flag to tell subsequent 401 handlers to stop processing.
- Guard the logout logic to only run if the signal hasn't been triggered yet.
- Use
takeUntilto 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 callnext()andcomplete()on it, all subsequent streams usingtakeUntil(this.logoutInProgress$)will immediately terminate.- Guard Clause: The check
!this.logoutInProgress$.closedensures we only run the logout logic once. Once the subject is completed, this check will fail for all future 401s. takeUntilOperator: 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




