刷新拦截器上的令牌时,SwitchMap 不会触发

问题描述 投票:0回答:1

我已经使用 Angular 在 Ionic 应用程序的 http 拦截器中实现了刷新令牌过程,我想我错过了一些让代码正常工作的东西:

refreshTokens 代币功能:

async refreshTokens() {
  return new Promise((resolve, reject) => {
    this.dataService
      .post(this.apiUriService.refreshTokenUri(), {
        refresh_token: this.authData.refreshToken,
      })
      .pipe(take(1))
      .subscribe({
        next: (res: any) => {
          if (!res) {
            return reject(null);
          }
          const resData = res.data ? res.data : null;
          this.utils.setItemOnLocalStorage(
            Constants.authDataStorageItemName,
            resData
          );
          resolve(resData);
        },
        error: (error) => {
          return reject(error);
        },
      });
  });
}

以及处理401Error函数:

async handle401Error(req: HttpRequest<any>, next: HttpHandler, error: any) {
  return from(this.refreshTokens()).pipe(
    switchMap((res: any) => {
      console.log('switchMap fired!');
      const request = this.addToken(req, res.access_token);
      return next.handle(request);
    }),
    catchError((error) => {
      this.logoutUser();
      return throwError(error);
    })
  );
}

这是完整的拦截器类代码:

import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import {
  HttpRequest,
  HttpResponse,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
  HttpErrorResponse,
} from '@angular/common/http';
import {
  Observable,
  throwError,
  BehaviorSubject,
  tap,
  take,
  from,
  switchMap,
  catchError,
} from 'rxjs';
import { AuthService } from './auth/auth.service';
import { ApiUriService } from './api-uri.service';
import { DataService } from './data.service';
import { Constants } from '../constants/constants';
import { CommonUtilsService } from './common-utils.service';
import { UserService } from './user/user.service';

@Injectable()
export class CommonHttpInterceptor implements HttpInterceptor {
  tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>('');

  get authData() {
    return this.authService.readUserInfoFromLocalStorage();
  }

  constructor(
    private authService: AuthService,
    private router: Router,
    private apiUriService: ApiUriService,
    private dataService: DataService,
    private userService: UserService,
    private utils: CommonUtilsService
  ) {}

  intercept(
    request: HttpRequest<unknown>,
    next: HttpHandler
  ): Observable<HttpEvent<unknown>> {
    const userInfoData = this.authService.authUserData;
    const headers: any = {};

    if (userInfoData && userInfoData.accessToken) {
      headers['Authorization'] = `Bearer ${userInfoData.accessToken}`;
    }

    const clonedRequest = request.clone({ setHeaders: headers });

    return next.handle(clonedRequest).pipe(
      tap(
        (event: HttpEvent<any>) => {
          if (event instanceof HttpResponse) {
            // case when we need to transform the response
          }
        },
        (error: HttpErrorResponse) => {
          // handle  refresh token in case of the access token is expired
          if (error.status === 401) {
            return this.handle401Error(request, next);
          }
          return throwError(error);
        }
      )
    );
  }

  logoutUser() {
    this.authService.doLogout();
    this.router.navigate(['auth/login']);
  }

  handle401Error(req: HttpRequest<any>, next: HttpHandler) {
    return from(this.refreshTokens()).pipe(
      switchMap((res: any) => {
        console.log('switchMap fired!');
        const request = this.addToken(req, res.access_token);
        return next.handle(request);
      }),
      catchError((error) => {
        this.logoutUser();
        return throwError(error);
      })
    );
  }

  // refresh access token OR logout user if request failed
  refreshTokens() {
    return new Promise((resolve, reject) => {
      this.dataService
        .post(this.apiUriService.refreshTokenUri(), {
          refresh_token: this.authData.refreshToken,
        })
        .pipe(take(1))
        .subscribe({
          next: (res: any) => {
            if (!res) {
              return reject(null);
            }
            const resData = res.data ? res.data : null;
            this.utils.setItemOnLocalStorage(
              Constants.authDataStorageItemName,
              resData
            );
            resolve(resData);
          },
          error: (error) => {
            return reject(error);
          },
        });
    });
  }

  //add token to authorization header
  private addToken(request: HttpRequest<any>, token: any) {
    const newToken = this.authService.shiftoCookieValue
      ? this.authService.shiftoCookieValue
      : token;
    if (newToken) {
      let clone: HttpRequest<any>;
      clone = request.clone({
        setHeaders: {
          Authorization: `Bearer ${token}`,
        },
      });
      return clone;
    }
    return request;
  }
}

我的问题是 switchMap 未触发并且未在控制台中记录任何日志。所以没有发送下一个请求。

angular ionic-framework
1个回答
0
投票

问题是您的错误处理程序方法在

tap()
内部被调用,并且返回了生成的 Observable。但在
tap()
中,返回值被忽略,因此 Observable 不会被订阅。

可以使用catchError,返回的Observable将被订阅。

return next.handle(clonedRequest).pipe(
  catchError((error: HttpErrorResponse) => {
    if (error.status === 401) {
      return this.handle401Error(request, next);
    }
    return throwError(() => error);
  })
);
© www.soinside.com 2019 - 2024. All rights reserved.