Angular Router Guard 在最终结果为真时未解析
Angular Router Guard not resolving when the end result is true
当最后的 if 语句为真时,以下代码可以正常工作。当最终 if 语句为 false 时,永远不会解析请求的路由。我试过添加等待和异步。我已经尝试将代码移动到一个单独的函数中,该函数 returns 一个带有布尔值的 await 并且没有任何东西可以在应该的时候解析路由。当它应该拒绝重定向到设置时它总是有效。
If语句
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;
}
return true;
完整路由器防护
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
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 ): any {
this.authService.userData.subscribe((observer1) => {
if(observer1) {
let subscriptions = this.subscriptionService.fetchUserSubscription(observer1.uid);
subscriptions.onSnapshot((observer:any) => {
observer.forEach((subscription:any) => {
if(subscription.exists) {
this.authService.allUserData.subscribe( async(userDataObserver:any) => {
let mod:string = state.url.split('/')[1];
await this.subscriptionService.fetchPlan(subscription.data().productID).then((plan:any) => {
let planLimit:number = parseInt(plan.metadata[mod]);
let companyUid:string = userDataObserver.companies[0].company;
this.afs.collection('companies').doc(companyUid).collection(mod).ref.get().then((moduleSnapshot:any) => {
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;
});
});
});
}
});
});
}
});
}
}
根据 Angular 的实现,canActivate
方法(CanActivate
接口需要)需要 return 类型。
export declare interface CanActivate {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;
}
无需深入研究路由守卫的逻辑,我能看到的是您实际上没有 returning 任何东西。因为 userData
是一个 Observable,所以订阅逻辑是异步处理的。这意味着 canActivate
方法由路由器调用,订阅逻辑异步启动,然后方法调用解析为无值。
要解决此问题,您需要 return 包含布尔值的 Observable 流。为此,我建议将 rxjs .pipe()
与 .switchMap()
运算符结合使用,以保留订阅逻辑。
return this.authService.userData.pipe(
switchMap((user) => {
// Handle subscription logic and return observable of a boolean value
return this.someService.doSomething();
})
);
当最后的 if 语句为真时,以下代码可以正常工作。当最终 if 语句为 false 时,永远不会解析请求的路由。我试过添加等待和异步。我已经尝试将代码移动到一个单独的函数中,该函数 returns 一个带有布尔值的 await 并且没有任何东西可以在应该的时候解析路由。当它应该拒绝重定向到设置时它总是有效。
If语句
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;
}
return true;
完整路由器防护
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
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 ): any {
this.authService.userData.subscribe((observer1) => {
if(observer1) {
let subscriptions = this.subscriptionService.fetchUserSubscription(observer1.uid);
subscriptions.onSnapshot((observer:any) => {
observer.forEach((subscription:any) => {
if(subscription.exists) {
this.authService.allUserData.subscribe( async(userDataObserver:any) => {
let mod:string = state.url.split('/')[1];
await this.subscriptionService.fetchPlan(subscription.data().productID).then((plan:any) => {
let planLimit:number = parseInt(plan.metadata[mod]);
let companyUid:string = userDataObserver.companies[0].company;
this.afs.collection('companies').doc(companyUid).collection(mod).ref.get().then((moduleSnapshot:any) => {
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;
});
});
});
}
});
});
}
});
}
}
根据 Angular 的实现,canActivate
方法(CanActivate
接口需要)需要 return 类型。
export declare interface CanActivate {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;
}
无需深入研究路由守卫的逻辑,我能看到的是您实际上没有 returning 任何东西。因为 userData
是一个 Observable,所以订阅逻辑是异步处理的。这意味着 canActivate
方法由路由器调用,订阅逻辑异步启动,然后方法调用解析为无值。
要解决此问题,您需要 return 包含布尔值的 Observable 流。为此,我建议将 rxjs .pipe()
与 .switchMap()
运算符结合使用,以保留订阅逻辑。
return this.authService.userData.pipe(
switchMap((user) => {
// Handle subscription logic and return observable of a boolean value
return this.someService.doSomething();
})
);