Angular 拦截器 - 执行异步操作

Angular Interceptor - perform async operation

我有以下拦截器,在访问令牌过期时用作重试包装器:

import { Injectable } from '@angular/core';
import {
  HttpEvent,
  HttpRequest,
  HttpHandler,
  HttpInterceptor,
  HttpHeaders,
} from '@angular/common/http';
import { Observable, EMPTY, throwError } from 'rxjs';
import { catchError, switchMap, take } from 'rxjs/operators';
import { Router } from '@angular/router';
import { AuthService } from '../services';

@Injectable()
export class UnauthorizedInterceptor implements HttpInterceptor {
  constructor(
    private router: Router,
    private readonly authService: AuthService
  ) {}

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError((error, caught) => {
        if (error.status === 401) {
          const newToken = this.authService.getNewToken();
          const updatedReq = req.clone({
            setHeaders: {
              Authorization: `Bearer ${newToken}`,
            },
          });
          return next.handle(updatedReq);
        }
        return EMPTY;
      })
    );
  }
}

按预期工作。 但是,函数 getNewToken 是一个异步函数,如果我将代码更新为

 intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError(async (error, caught) => {
        if (error.status === 401) {
          const newToken = await this.authService.getNewToken();
          const updatedReq = req.clone({
            setHeaders: {
              Authorization: `Bearer ${newToken}`,
            },
          });
          return next.handle(updatedReq);
        }
        return EMPTY;
      })
    );
  }

当需要在 catchError 中执行异步操作时,此用例的解决方案是什么?

P.SgetNewToken 函数可以重写为 return observable 而不是 promise 如果这有帮助的话,但是我'我想不通

更新

TypeScript抛出的错误是

Type 'Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any> | Observable<...>>' is not assignable to type 'Observable<HttpEvent<any>>'.
  Type 'HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any> | Observable<...>' is not assignable to type 'HttpEvent<any>'.
    Type 'Observable<HttpEvent<any>>' is not assignable to type 'HttpEvent<any>'.ts(2322)

如果我执行 @ts-ignore 那么在浏览器中抛出的错误是

 EmptyError: no elements in sequence

试试这个方法:

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
      catchError((error: HttpErrorResponse) => {
        if (error.status === 401) {
          return this.handleUnauthorized(request, next);
        } else {
          return throwError(error);
        }
      })
    );
  }

以及“handleUnauthorized”方法的实现


  handleUnauthorized(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
    if (!this.isRefreshingToken) {
      this.isRefreshingToken = true;

      // Reset here so that the following requests wait until the token
      // comes back from the refreshToken call.
      this.authService.setToken(null);

      const body = new HttpParams()
        .set('grant_type', 'refresh_token')
        .set('refresh_token', localStorage.getItem('refresh_token'));
      return this.http.post('YOUR_URL', body.toString(), {
        headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
      }).pipe(flatMap((el: any) => {
        if (el && el.access_token) {
          req = req.clone({ setHeaders: { Authorization: 'Bearer ' + el.access_token } });
          this.authService.setToken(el);
          return next.handle(req);
        } else {
          this.authService.unAuthorize();
          return throwError('refreshToken');
        }
      }), finalize(() => {
        this.isRefreshingToken = false;
      }), catchError(error => {
        return throwError(error);
      }));
    } else {
      return this.authService.tokenSubject.pipe(
        filter(token => token)
        , take(1)
        , switchMap(token => {
          req = req.clone({ setHeaders: { Authorization: 'Bearer ' + token.access_token } });
          this.authService.setToken(token);
          return next.handle(req);
        }));
    }
  }

并且在 authService 内部,您应该具有此功能

  tokenSubject = new Subject<any>();
  .
  .
  .
  setToken(newToken: any) {
    this.tokenSubject.next(newToken)
  }

  getToken() {
    return this.tokenSubject.asObservable();
  }

希望这个方法对你有帮助,有什么问题可以在评论区问我