Как вы можете использовать Angular canActivate, чтобы отрицать результат защиты?

Из Angular документации на canActivate, похоже, вы можете использовать только защитные устройства canActivate, чтобы разрешить переход на маршрут, если функция canActivate в конечном итоге возвращает true.

Можно ли сказать, "только переходите к этому маршруту, если класс 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!

Ответ 1

Интересным в вашем вопросе является формулировка:

Есть ли способ сказать: "Переходите к этому маршруту, если класс canActivate оценивается как false"

И как вы выразили "интуитивное" решение:

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

Что в основном говорит, вам нужно negate результат [email protected]

Рассмотрим следующую реализацию 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. Если по какой-то причине реализация [email protected] изменится, 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

Ответ 2

Размышляя о вашей проблеме, одним из решений может быть реализация защиты маршрута, которая делает логику обратным.

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
    }
}

Ответ 3

У меня была похожая проблема - я хотел создать страницу входа, которая была бы доступна только в том случае, если она не прошла проверку подлинности, а панель мониторинга - только в том случае, если вы прошли проверку подлинности (и перенаправить пользователя на соответствующую страницу автоматически). Я решил это, сделав сам логин охранника + маршрут чувствительным:

Маршруты:

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;
      }
    }
  }
}