【问题标题】:How can you use Angular's canActivate to negate the result of a guard?如何使用 Angular 的 canActivate 来否定守卫的结果?
【发布时间】:2019-10-06 06:39:40
【问题描述】:

From the Angular documentation on canActivate,如果canActivate 函数最终返回true,您似乎只能使用canActivate 守卫来允许继续执行路由。

有没有办法说,“只有在 canActivate 类的计算结果为 false 时才继续这条路线”?

例如,不允许登录的用户访问登录页面,我尝试了这个但它不起作用:

export const routes: Route[] = [
    { path: 'log-in', component: LoginComponent, canActivate: [ !UserLoggedInGuard ] },

我在控制台中收到此错误:

ERROR Error: Uncaught (in promise): Error: StaticInjectorError[false]: 
  StaticInjectorError[false]: 
    NullInjectorError: No provider for false!
Error: StaticInjectorError[false]: 
  StaticInjectorError[false]: 
    NullInjectorError: No provider for false!

【问题讨论】:

  • 你不能否定一个类型,那是完全错误的
  • 用这个登录页面的特殊情况。我做了一个完全独立的守卫,比如AuthRedirectGuard,它检查用户是否登录。如果为真,则重定向到登录页面,否则继续登录。

标签: angular typescript angular-routing canactivate angular-guards


【解决方案1】:

你的问题中有趣的是公式:

有没有办法说,“只有在 canActivate 类评估为假" ?

以及您如何表达“直观”的解决方案:

{ path: 'log-in', component: LoginComponent, canActivate: [ !UserLoggedInGuard ] },

基本上就是说,你需要negateUserLoggedInGuard@canActivate的结果

让我们考虑以下UserLoggedInGuard 的实现:

@Injectable()
export class UserLoggedInGuard implements CanActivate {
   constructor(private _authService: AuthService) {}

   canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        return this._authService.isLoggedIn();
    }
} 

接下来,让我们看看@Mike提出的解决方案

@Injectable()
export class NegateUserLoggedInGuard implements CanActivate {    
    constructor(private _authService: AuthService) {}

   canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        return !this._authService.isLoggedIn();
    }
}

现在,该方法还可以,但与 UserLoggedInGuard 的(内部)实现紧密耦合。如果由于某种原因UserLoggedInGuard@canActivate 的实现发生变化,NegateUserLoggedInGuard 将中断。

我们如何避免这种情况?简单,滥用依赖注入:

@Injectable()
export class NegateUserLoggedInGuard implements CanActivate {    
  constructor(private _userLoggedInGuard: UserLoggedInGuard) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
     return !this._userLoggedInGuard.canActivate(route,state);
  }
}

现在这正是你所表达的

canActivate: [ !UserLoggedInGuard ]

最好的部分:

  • 它没有与UserLoggedInGuard 的内部实现紧密耦合
  • 可以扩展以操作多个Guard 类的结果

【讨论】:

  • 好主意。我还将两个导出的类放在同一个文件中,并且可能使实际的 canActivate 函数定义成为它们之间通用的单独外部函数
  • 我真的没有看到通过函数分享实现的充分理由。我会简单地使用 DI 来组成这个否定保护包装器
  • 我也不建议将两个类放在一个文件中。如果否定守卫现在否定多个守卫的结果怎么办?你会把它放在哪个文件中?
  • 完美答案!
  • 然而,当 'isLoggedIn' 方法返回 Observable 时,这不起作用
【解决方案2】:

我遇到了类似的问题 - 想要创建一个登录页面,该页面只有在未通过身份验证时才可访问,而仪表板只有在您通过身份验证时才能访问(并自动将用户重定向到适当的登录页面)。我通过使警卫本身登录+路由敏感来解决它:

路线:

const appRoutes: Routes = [
  { path: 'login', component: LoginComponent, canActivate: [AuthGuard] },
  { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },

守卫:

export class AuthGuard implements CanActivate {

  private login: UrlTree;
  private dash: UrlTree;

  constructor(private authSvc: AuthenticationService, private router: Router ) {
    this.login = this.router.parseUrl('login');
    this.dash = this.router.parseUrl('dashboard');
  }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree {
    if (this.authSvc.isSignedIn()) {
      if (route.routeConfig.path === 'login') {
        return this.dash;
      } else {
        return true;
      }
    } else {
      if (route.routeConfig.path === 'login') {
        return true;
      } else {
        return this.login;
      }
    }
  }
}

【讨论】:

    【解决方案3】:

    考虑到您的问题,一种解决方案可能是实现一个反向执行逻辑的路由保护。

    import { MyService } from "./myservice.service";
    import { CanActivate, RouterStateSnapshot, ActivatedRouteSnapshot } from "@angular/router";
    import { Injectable } from "@angular/core";
    
    @Injectable()
    export class MyGuard implements CanActivate {
    
        constructor(private myService: MyService) {}
    
        canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
            return this.myService.isNotLoggedIn(); //if user not logged in this is true
        }
    }
    

    【讨论】:

    • 所以基本上为not 情况创建了一个完全独立的警卫?
    • @CodyBugstein 正是因为你想要反向
    • 对,这就是我现在正在使用的解决方案。但这不是一个理想的解决方案,因为我将不得不将我的守卫数量增加一倍并且有很多重复的代码
    • 这是没有办法的人 :( 除非你想在服务中创建一个 bool isNotLoggedIn 并用它来评估你的普通后卫?
    • 不确定你的意思
    猜你喜欢
    • 2017-07-26
    • 1970-01-01
    • 2018-01-02
    • 2017-08-31
    • 2021-09-13
    • 2021-11-21
    • 2022-06-21
    • 2017-11-11
    • 2020-02-14
    相关资源
    最近更新 更多