Angular5+Ionic3:使用HTTP拦截器从Ionic Storage设置请求头问题
解决Angular拦截器首次启动时Token异步获取的问题
这个问题我太熟悉了——异步操作的时序坑确实容易在App首次加载时找上门!你说得没错,构造函数里的storage.get('token')是异步Promise,而拦截器的intercept方法会同步执行,导致首次请求时this.token还没被赋值,自然就带上了undefined。
核心解决方案:把拦截逻辑改成异步处理
既然Storage.get()返回的是Promise,我们可以把它转换成Observable,确保拿到Token之后再克隆请求并发送。这样不管是首次启动还是后续请求,都能保证请求头里的Token是有效的。
步骤1:导入必要的RxJS操作符
首先补充导入from(把Promise转成Observable)和switchMap(处理异步流的切换):
import { Injectable } from '@angular/core'; import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; import { Observable, from } from "rxjs"; import { switchMap } from 'rxjs/operators'; import { Storage } from '@ionic/storage'; import { Globals } from '../globals/globals';
步骤2:修改拦截器的intercept方法
把原来同步的逻辑改成异步流处理:
@Injectable() export class Interceptor implements HttpInterceptor { constructor(private storage: Storage, private global: Globals){} intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { // 把Storage的Promise转换成Observable,确保拿到Token后再处理请求 return from(this.storage.get('token')).pipe( switchMap(token => { // 这里token已经成功获取,即使是首次启动也没问题 const modifiedRequest = req.clone({ setHeaders: { 'Token': token || '', // 兜底处理token为null/undefined的情况 'Version': this.global.version, } }); return next.handle(modifiedRequest); }) ); } }
优化:加入内存缓存提升性能
如果担心每次请求都去读Storage影响性能,可以在拦截器里维护一个内存级的Token缓存,后续请求直接用缓存值,不用重复读取Storage:
@Injectable() export class Interceptor implements HttpInterceptor { private cachedToken: string | null = null; // 内存缓存Token constructor(private storage: Storage, private global: Globals){} intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { let tokenObservable: Observable<string | null>; if (this.cachedToken) { // 内存有缓存,直接使用 tokenObservable = of(this.cachedToken); } else { // 内存无缓存,从Storage读取并更新缓存 tokenObservable = from(this.storage.get('token')).pipe( tap(token => this.cachedToken = token) ); } return tokenObservable.pipe( switchMap(token => { const modifiedRequest = req.clone({ setHeaders: { 'Token': token || '', 'Version': this.global.version, } }); return next.handle(modifiedRequest); }) ); } }
记得额外导入of和tap操作符:
import { Observable, from, of } from "rxjs"; import { switchMap, tap } from 'rxjs/operators';
这样修改后,首次启动App时,拦截器会等待Token从Storage读取完成后再发送请求,彻底解决undefined的问题;后续请求则直接用内存缓存,兼顾了正确性和性能。
内容的提问来源于stack exchange,提问作者nishil bhave
相关产品推荐
相关产品推荐

