Angular Router Guard 最终返回true时无法解析路由问题
问题根因
你遇到的路由放行失败核心原因是CanActivate接口要求的返回值没有被正确返回:
- 你代码里的
return true/return false都是嵌套在多层异步回调(subscribe回调、onSnapshot回调、then回调)内部的,这些返回值只会结束当前回调函数,不会传递给外层的canActivate方法 - 外层
canActivate方法执行完所有同步逻辑后直接返回了undefined,Angular路由守卫没有拿到明确的放行/拦截信号,所以不会执行路由解析逻辑 - 另外你当前写法存在大量未取消的流订阅,会有内存泄漏风险,且嵌套回调层级过深可读性差
修复后的完整代码
import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; import { Observable, of } from 'rxjs'; import { switchMap, map, catchError, take } from 'rxjs/operators'; import { ToastrService } from 'ngx-toastr'; import { AngularFirestore } from '@angular/fire/firestore'; import { AuthService } from '../services/auth/auth.service'; import { SubscriptionsService } from '../services/subscriptions/subscriptions.service'; @Injectable({ providedIn: 'root' }) export class SubscriptionGuard implements CanActivate { constructor( private router: Router, private toast: ToastrService, private authService: AuthService, private subscriptionService: SubscriptionsService, private afs: AngularFirestore ) { } canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { // 用take(1)只取一次流数据,避免内存泄漏 return this.authService.userData.pipe( take(1), switchMap(observer1 => { // 用户未登录直接拦截,可根据自己的逻辑调整 if (!observer1) return of(false); // 把onSnapshot转成Observable return new Observable(observer => { const unsubscribe = this.subscriptionService.fetchUserSubscription(observer1.uid) .onSnapshot(snapshot => { observer.next(snapshot); }, err => observer.error(err)); // 退订逻辑 return () => unsubscribe(); }).pipe(take(1)); }), switchMap((subscriptionSnapshot: any) => { // 取第一个存在的订阅 const validSub = subscriptionSnapshot.docs.find((sub: any) => sub.exists); if (!validSub) return of(false); // 取用户数据,同时把有效订阅信息传递到下游 return this.authService.allUserData.pipe( take(1), map(userData => ({ validSub, userData })) ); }), switchMap(async (data: any) => { const { validSub, userData: userDataObserver } = data; const mod = state.url.split('/')[1]; // 拉取套餐信息 const plan = await this.subscriptionService.fetchPlan(validSub.data().productID); const planLimit = parseInt(plan.metadata[mod]); const companyUid = userDataObserver.companies[0].company; // 拉取模块数据量 const moduleSnapshot = await this.afs.collection('companies') .doc(companyUid).collection(mod).ref.get(); if (moduleSnapshot.size >= planLimit) { this.toast.info(`You've reached your plan maximum, upgrade to add more ${mod}.`, 'Plan Maximum'); this.router.navigateByUrl('/settings/profile/subscription'); return false; } console.log('Plan max not met, should resolve'); return true; }), // 异常情况默认拦截 catchError(() => of(false)) ); } }
关键优化说明
- 所有异步逻辑都通过RxJS操作符串联,最终直接返回Observable给
canActivate,Angular可以正确拿到返回值判断是否放行 - 所有流订阅都加了
take(1),拿到一次数据就主动结束订阅,避免内存泄漏 - 去掉了多层嵌套的subscribe回调,逻辑层级更清晰
- 增加了异常捕获逻辑,避免异步报错导致路由守卫无响应
内容的提问来源于stack exchange,提问作者SDMitch
相关产品推荐
相关产品推荐

