我知道当canActivate函数返回一个简单的boolean
时,角度路径保护按指定的顺序执行,但是,如果守卫返回类型Observable<boolean>
或Promise<boolean>
怎么办?
路线示例:
{
path: 'confirm',
canActivate: [AuthGuard, SessionExpiredAuthGuard, CheckoutAuthGuard],
component: CheckoutReviewOrderComponent
},
SessionExpiredAuthGuard和CheckoutAuthGuard都返回类型Observable<boolean>
。我不希望在SessionExpiredAuthGuard完成从异步http请求中检索数据之前执行CheckoutAuthGuard。
有没有办法强制这些异步防护按顺序执行?
答案 0 :(得分:8)
首先,角度并不支持将警卫串联起来的功能。因此,如果第一个警卫是异步的并且正在尝试进行ajax调用,那么即使在警卫1中完成ajax请求之前,所有剩余的警卫也会被解雇。
我遇到了类似的问题,这就是我解决它的方法 -
我们的想法是创建一个主守卫,并让主守卫处理其他守卫的执行。
在这种情况下,路由配置将包含主控后卫作为唯一的后卫。
要让主人知道要为特定路线触发的警卫,请在data
中添加Route
属性。
data
属性是一个键值对,允许我们使用路径附加数据。
然后可以使用警卫中ActivatedRouteSnapshot
方法的canActivate
参数在警卫中访问数据。
解决方案看起来很复杂,但一旦将其集成到应用程序中,它将确保警卫的正常工作。
以下示例解释了这种方法 -
<强> 1。常量对象以映射所有应用程序保护 -
export const GUARDS = {
GUARD1: "GUARD1",
GUARD2: "GUARD2",
GUARD3: "GUARD3",
GUARD4: "GUARD4",
}
<强> 2。 Application Guard -
import { Injectable } from "@angular/core";
import { Guard4DependencyService } from "./guard4dependency";
@Injectable()
export class Guard4 implements CanActivate {
//A guard with dependency
constructor(private _Guard4DependencyService: Guard4DependencyService) {}
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
return new Promise((resolve: Function, reject: Function) => {
//logic of guard 4 here
if (this._Guard4DependencyService.valid()) {
resolve(true);
} else {
reject(false);
}
});
}
}
第3。路由配置 -
import { Route } from "@angular/router";
import { View1Component } from "./view1";
import { View2Component } from "./view2";
import { MasterGuard, GUARDS } from "./master-guard";
export const routes: Route[] = [
{
path: "view1",
component: View1Component,
//attach master guard here
canActivate: [MasterGuard],
//this is the data object which will be used by
//masteer guard to execute guard1 and guard 2
data: {
guards: [
GUARDS.GUARD1,
GUARDS.GUARD2
]
}
},
{
path: "view2",
component: View2Component,
//attach master guard here
canActivate: [MasterGuard],
//this is the data object which will be used by
//masteer guard to execute guard1, guard 2, guard 3 & guard 4
data: {
guards: [
GUARDS.GUARD1,
GUARDS.GUARD2,
GUARDS.GUARD3,
GUARDS.GUARD4
]
}
}
];
<强> 4。大师卫队 -
import { Injectable } from "@angular/core";
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from "@angular/router";
//import all the guards in the application
import { Guard1 } from "./guard1";
import { Guard2 } from "./guard2";
import { Guard3 } from "./guard3";
import { Guard4 } from "./guard4";
import { Guard4DependencyService } from "./guard4dependency";
@Injectable()
export class MasterGuard implements CanActivate {
//you may need to include dependencies of individual guards if specified in guard constructor
constructor(private _Guard4DependencyService: Guard4DependencyService) {}
private route: ActivatedRouteSnapshot;
private state: RouterStateSnapshot;
//This method gets triggered when the route is hit
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
this.route = route;
this.state = state;
if (!route.data) {
Promise.resolve(true);
return;
}
//this.route.data.guards is an array of strings set in routing configuration
if (!this.route.data.guards || !this.route.data.guards.length) {
Promise.resolve(true);
return;
}
return this.executeGuards();
}
//Execute the guards sent in the route data
private executeGuards(guardIndex: number = 0): Promise<boolean> {
return this.activateGuard(this.route.data.guards[guardIndex])
.then(() => {
if (guardIndex < this.route.data.guards.length - 1) {
return this.executeGuards(guardIndex + 1);
} else {
return Promise.resolve(true);
}
})
.catch(() => {
return Promise.reject(false);
});
}
//Create an instance of the guard and fire canActivate method returning a promise
private activateGuard(guardKey: string): Promise<boolean> {
let guard: Guard1 | Guard2 | Guard3 | Guard4;
switch (guardKey) {
case GUARDS.GUARD1:
guard = new Guard1();
break;
case GUARDS.GUARD2:
guard = new Guard2();
break;
case GUARDS.GUARD3:
guard = new Guard3();
break;
case GUARDS.GUARD4:
guard = new Guard4(this._Guard4DependencyService);
break;
default:
break;
}
return guard.canActivate(this.route, this.state);
}
}
这种方法的挑战之一是重构现有的路由模型。但是,它可以部分完成,因为更改不会中断。
我希望这会有所帮助。
答案 1 :(得分:0)
除了答案planet_hunter之外,我还敢分享一点改进master-guard
答案 2 :(得分:0)
这是我受@planet_hunter启发的解决方案,它与Angular 8的 CanActivate 签名完全兼容: