按顺序执行多个异步路由守卫
Posted
技术标签:
【中文标题】按顺序执行多个异步路由守卫【英文标题】:Execute Multiple Asynchronous Route Guards in Order 【发布时间】:2017-11-22 06:59:37 【问题描述】:我知道当 canActivate 函数返回一个简单的boolean
时,角度路由守卫会按指定的顺序执行,但是,如果守卫返回类型为Observable<boolean>
或Promise<boolean>
怎么办?
路线示例:
path: 'confirm',
canActivate: [AuthGuard, SessionExpiredAuthGuard, CheckoutAuthGuard],
component: CheckoutReviewOrderComponent
,
SessionExpiredAuthGuard 和 CheckoutAuthGuard 都返回类型 Observable<boolean>
。我不希望在 SessionExpiredAuthGuard 完成从异步 http 请求中检索其数据之前执行 CheckoutAuthGuard。
有没有办法强制这些异步守卫按顺序执行?
【问题讨论】:
你真的见过他们的行为不正常吗?我认为它们已通过concatAll
解决,这会将它们称为串联而不是并联。
是的。有时 CheckoutAuthGuard 在前一个之前完成。
@jonrsharpe,但是我在我的 SessionExpiredAuthGuard 中使用finally
方法...这会在整个 observables 堆栈上最后执行吗?
【参考方案1】:
问题
首先,Angular 不支持串联调用守卫的功能。因此,如果第一个守卫是异步的并且正在尝试进行 ajax 调用,那么所有剩余的守卫甚至在守卫 1 中的 ajax 请求完成之前都会被触发。
我遇到了类似的问题,这就是我解决它的方法 -
解决方案
想法是创建一个master guard,让master guard来处理其他guard的执行。
在这种情况下,路由配置将包含主守卫作为唯一守卫。
要让主守卫知道特定路由要触发的守卫,请在Route
中添加data
属性。
data
属性是一个键值对,允许我们在路由中附加数据。
然后可以使用警卫中canActivate
方法的ActivatedRouteSnapshot
参数在警卫中访问数据。
该解决方案看起来很复杂,但一旦将其集成到应用程序中,它将确保警卫正常工作。
以下示例解释了这种方法 -
示例
1.用于映射所有应用程序守卫的常量对象 -
export const GUARDS =
GUARD1: "GUARD1",
GUARD2: "GUARD2",
GUARD3: "GUARD3",
GUARD4: "GUARD4",
2。应用程序防护 -
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);
挑战
这种方法的挑战之一是重构现有的路由模型。但是,由于更改不会中断,因此可以部分完成。
我希望这会有所帮助。
【讨论】:
我在“return guard.canActivate(this.route, this.state);”上遇到错误错误是:键入'布尔值| Observable除了回答planet_hunter,我也敢分享一点改进master-guard
【讨论】:
【参考方案3】:这是我受@planet_hunter 启发的解决方案,它与 Angular 8 的 CanActivate 签名完全兼容:
Multiple canActivate guards all run when first fails
【讨论】:
以上是关于按顺序执行多个异步路由守卫的主要内容,如果未能解决你的问题,请参考以下文章