在 NGRX 效果中加载数据,并在使用路由防护之前调度操作



我的身份验证基于NGRX,所以当页面开始加载时,我得到了所有角色和 然后登录。

但是当我开始使用路由保护时, 路由葫芦在用户数据加载之前开始工作

我如何等待用户加载操作完成然后开始使用canActivate我尝试以下解决方案,但它不起作用

export class AuthGuard implements CanActivate, OnDestroy {
private unsubscribe: Subject<any>;
constructor(private store: Store<AppState>, private router: Router,
private alertService: ToastrService, private authService: AuthService) {
this.unsubscribe = new Subject();
}
ngOnDestroy(): void {
this.unsubscribe.next();
this.unsubscribe.complete();
}
getFromStoreOrAPI(): Observable<any> {
return this.store.pipe(
select(isUserLoaded),
tap((data: boolean) => {
if (!data) {
this.store.dispatch(new UserRequested());
}
}),
take(1)
);
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.getFromStoreOrAPI().pipe(
switchMap(() =>
this.store.pipe(
select(isLoggedIn),
map(loggedIn => {
if (!loggedIn) {
this.router.navigateByUrl('/auth/login');
return false;
} else {
this.store.pipe(
select(currentUserRoles),
map((userRoles: Role[]) => {
//.......
}),
takeUntil(this.unsubscribe)
).subscribe();
}
}),
)
),
catchError(() => this.router.navigateByUrl('/auth/login'))
);
}
}

您可以使用filter等待loaded标志被true

这是我用auth.guard.ts采取的方法:

canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> {
return this.authFacade.loaded$.pipe(
filter(loaded => !!loaded),
mergeMap(() => this.authFacade.userAccount$),
map(userAccount => {
if (!userAccount) this.authFacade.redirectLoginPage(state.url);
return !!userAccount;
}),
first()
);
}

就我而言,主要应用程序组件正在调度一个操作CheckAuth以检查用户是否已通过身份验证,然后设置loaded标志。

它应该根据您的需要进行一些调整。但主要区别在于使用filter,如果用户检查未完成,则避免继续工作流,并强制等待值。

当然,请务必在收到响应(无论是否经过身份验证(后或发生任何错误时在所有情况下设置loaded值。

以下是针对您的情况的潜在改编:

authLoaded$ = this.store.pipe(select(authLoaded));
authAccount$ = this.store.pipe(select(authAccount));
canActivate(...) {
return userLoaded$.pipe(    
tap(loaded => {
if (!loaded) {
this.store.dispatch(new UserRequested());
}
}),
filter(loaded => !!loaded),
mergeMap(() => authAccount$),
map(authAccount => {
if (!authAccount.loggedIn) {
this.router.navigateByUrl('/auth/login');
return false;
}
if (!authAccount.roles?.length) {
this.router.navigateByUrl('/auth/forbidden');
return false;
}
// do some extra stuff...
return true;
}),
first()
);
}

我将isUserLoaded重命名为authLoaded以清楚地指示身份验证加载的状态(例如,您也可以使用就绪或不就绪(。但不是必需的用户。

我还创建了一个新的选择器authAccount它返回一个至少包含 2 件事的对象:

登录
  • :如果用户已登录,则为真/假
  • 角色
  • :用户角色数组。 但是您当然可以添加user属性,其中包含用户详细信息。

这是来自您所在州不同部分的组合选择器。 有了它,您的代码更加清晰和可维护,您将获得当前身份验证用户的完整状态。

也许有些错别字是可能的,我直接在我的答案中编写了代码,没有测试它。 希望这对您有所帮助。