订阅路由守卫中的可观察对象及其含义

时间:2018-10-22 19:06:35

标签: angular rxjs6 angular-observable angular-guards

我有一个名为PermissionGuard的路由器保护程序,是在此处启动的

const routes: Routes = [
  {
    path: ':company',
    component: CompanyComponent,
    canActivate: [PermissionGuard],
    canActivateChild: [PermissionGuard],
    children: [
      {
        path: '',
        component: IndexComponent
      },
      {
        path: 'projects',
        loadChildren: '../projects/projects.module#ProjectsModule'
      },
    ]
  }
];

在我的PermissionGuard内,我这样订阅PermissionService

export class PermissionGuard implements CanActivate, CanActivateChild {

  private permissions: Permission[];

  constructor(private permissionService: PermissionService, private router: Router) {
    this.permissionService.permissions$.subscribe(
      (permissions: Permission[]) => {
        this.permissions = permissions;
      }
    );
  }

  canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    this.permissionService.getPermissions(next.paramMap.get('company'));
    return this.permissionService.permissionsFetchDone$
    .pipe(filter(x => x))
    .pipe(
      map(response => {
        if (this.permissions) {
          return true;
        } else {
          this.router.navigate(['/forbidden']);
        }
      })
    );
  }
}

,然后根据此数据执行必要的canActivatecanActivateChild检查。通过在子级路由中添加console.log()并从permissions$发出新数据,我可以看到即使守卫已“使用”且路由已激活,可观察对象仍处于活动状态。然后,我期望当我走到path: ':company'以外的路线时它会消失,但是警卫并未被摧毁。

这使我想到我的问题: 我这样做正确吗?我希望使用防护措施来检查用户是否具有任何权限,但是同时我只想执行一次HTTP请求权限(在导航到path: ':company'或其任何子级时)。恐怕如果使用这样的防护措施,由于有大量的观察者,它会及时减慢整个应用程序的速度。

1 个答案:

答案 0 :(得分:2)

  

从子级路由内的permissions $中释放新数据我可以看到,即使防护已被“使用”且路由已激活,可观察对象仍处于活动状态。

首先,permissions$仍处于活动状态,因为您从未退订过自己的后卫。棱角分明的护卫为单身。

即使守护程序不是单例,在订阅中仍通过this.permissions引用守护程序的实例,并且如果您的observable作为auth服务中的变量存在(我假设是单例),则此绑定将还可以防止垃圾收集。

  

这使我想到了一个问题:我这样做正确吗?我希望使用防护措施来检查用户是否具有任何权限,

最好在您的监护人那里发出请求,例如获取权限。

  

但同时我只想对权限执行一次HTTP请求(当导航到path:':company'或其任何子级时)。

如果您只想发出一次请求,则应考虑在身份验证服务中使用shareReplay,以便将来所有连接的实例都使用相同的先前发出的值。