延迟加载模块上带有第二个 canActivate 守卫的路由器无限循环

Router infinite loop with second canActivate guard on lazy-loaded modules

我有一个带有延迟加载模块的 angular 4.3.6 应用程序。这是部分根路由器:

const routes: Routes = [
  { path: '', redirectTo: 'fleet', pathMatch: 'full' },
  {
    path: '',
    component: AppComponent,
    canActivate: [AuthenticationGuard],
    children: [
      {
        path: 'fleet',
        loadChildren: "./modules/fleet.module",
        canActivate: [AuthenticationGuard]
      },
      {
        path: 'password/set',
        loadChildren: "./modules/chooseNewPassword.module",
        canActivate: [ChoosePasswordGuard]
      }
    ]
  }
]
// Exports RouterModule.forRoot(routes, { enableTracing: true });

我在这两个示例模块中的子路由器:

舰队:

RouterModule.forChild([
  {
    path: '',
    component: FleetComponent,
    canActivate: [AuthenticationGuard]
  }
]);

选择新密码:

RouterModule.forChild([
  {
    path: '',
    component: ChooseNewPasswordComponent,
    canActivate: [ChoosePasswordGuard]
  }
]);

AuthenticationGuard 调用如下所示的方法:

return this.getUserSession().map((userSession: UserSession) => {
  if (userSession && userSession.ok) {
    return true;
  }
  else if (userSession && userSession.expired) {
    this.router.navigate(['password/set'])
      .catch((e: Error) => console.error(e));
    return true;
  }
  else {
    window.location.replace('/');
    return false;
  }
}

因此,如果用户的会话正常,它会激活路由。如果用户密码过期,它将用户重定向到选择新密码模块。如果没有会话,则重定向到登录。

ChoosePasswordGuard做类似的事情,但只保护选择新密码组件(不同的工具用于一般设置密码):

return this.getUserSession().map((userSession: UserSession) => {
  if (userSession) {
    return userSession.expired;
  }
  else {
    return false;
  }
});

这在模块拆分之前有效。

现在,我陷入了重定向循环。启用路由器跟踪后,我观察到以下顺序。用户登录并且 AuthenticationGuard 更正重定向到 /password/set 模块,并移交给 ChooseNewPasswordGuard:

  1. NavigationStart(id: 4, url: '/password/set')
  2. RoutesRecognized {id: 4, url: "/password/set", urlAfterRedirects: "/password/set",状态:RouterStateSnapshot}
  3. GuardsCheckStart {id: 4, url: "/password/set", urlAfterRedirects: UrlTree, state: RouterStateSnapshot}
  4. GuardsCheckEnd {id: 4, url: "/password/set", urlAfterRedirects: UrlTree, state: RouterStateSnapshot, shouldActivate: true}
  5. NavigationCancel {id: 4, url: "/password/set", 原因: ""}

然后这个循环重复。

(如果我用 return Observable.of(true); 替换整个 ChooseNewPasswordGuard 也会重复)

编辑:即使我在 URL 栏中提供 /#/password/set,我也会被重定向到根页面 (/)...

问题:

  1. 既然模块是延迟加载的,我在我的路由器或守卫中做错了什么来强制这个循环?我对 shouldActivate: true 后跟 NavigationCancel reason: "".

  2. 特别困惑
  3. 是不是和我直接在AuthenticationGuard中重定向,现在这个guard应用到我的主空根路由({ path: '', redirectTo: 'fleet', pathMatch: 'full' })有关系吗总是调用和重定向,即使我设置了路径?

  4. 我真的需要在子路由和根路由中重复 canActivate 守卫吗?

  5. 像往常一样,欢迎任何其他评论。

问题是我过度应用了 AuthenticationGuard:它不应该应用到顶级 AppComponent 因为它总是会重定向到选择新密码模块,即使它正在加载那个模块。

我的根 routes 应该是这样的:

const routes: Routes = [
  { path: '', redirectTo: 'fleet', pathMatch: 'full' },
  {
    path: '',
    component: AppComponent,
    // canActivate: [AuthenticationGuard], // <-- Remove this guard
    children: [
      {
        path: 'fleet',
        loadChildren: "./modules/fleet.module",
        canActivate: [AuthenticationGuard]
      },
      {
        path: 'password/set',
        loadChildren: "./modules/chooseNewPassword.module",
        canActivate: [ChoosePasswordGuard]
      }
    ]
  }
]

(我欢迎并乐意接受更好的解释或更好的 AuthenticationGuard 模式。)