从 canActivate
上的 Angular 文档来看,如果
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!
你的问题中有趣的是它的表述:
有没有什么办法可以说“只有在以下情况下才继续走这条路线” canActivate 类的计算结果为 false”?
以及你如何表达“直观”的解决方案:
{ path: 'log-in', component: LoginComponent, canActivate: [ !UserLoggedInGuard ] },
基本上就是说,您需要
negate
UserLoggedInGuard@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
类的结果我遇到了类似的问题 - 想要制作一个只有在未经过身份验证时才可以访问的登录页面,以及只有经过身份验证才可以访问的仪表板(并自动将用户重定向到适当的页面)。我通过让守卫本身对登录+路由敏感来解决这个问题:
路线:
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;
}
}
}
}
考虑您的问题,一种解决方案可能是实现一个反向执行逻辑的路由防护。
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
}
}
我在所有路线中添加了
data.auth: boolean
选项(除了那些应该可以通过任何方式访问的路线),如下所示:
const routes: Routes = [
{
path: '', // all
component: HomeComponent,
},
{
path: 'authenticate', // not authenticated
component: AuthComponent,
data: { auth: false },
canActivate: [AuthGuard],
},
{
path: 'account', // authenticated
component: AccountComponent,
data: { auth: true },
canActivate: [AuthGuard],
},
]
然后在我的身份验证防护中,我检查此数据属性并采取相应措施:
export class AuthGuard implements CanActivate {
constructor(private readonly supabase: SupabaseService) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return route.data['auth'] ? !!this.supabase.session : !this.supabase.session
}
}