我观察到我的 CanActivate 守卫(Angular 10.2.5)的奇怪行为:
export class VersionGuardService implements CanActivate {
constructor(private router: Router, private http: HttpClient){
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
console.log("In the Guard");
return this.http.request<any>('get',`https://jsonplaceholder.typicode.com/users`).pipe(
tap(resp => console.log("Response: ", resp)),
map(resp => false)
);
//return of(false);
}
}
app-routing.module.ts 中的路由如下所示:
const routes: Routes = [
{
path: 'login',
component: LoginComponent,
canActivate: [VersionGuardService]
},
{
path: 'logout',
component: LogoutComponent
},
{
path: 'error',
component: ErrorComponent
},
{
path: '',
redirectTo: 'login',
pathMatch: 'full'
}
];
如果我用上面的代码调用应用程序,我会收到 Too many calls to Location or History APIs within a short timeframe
错误(在 Firefox 中)。当我在浏览器中检查控制台输出时 - 确实有很多(近 200 个)带有 In the Guard
消息的条目。如果我检查开发工具中的网络选项卡,我会在网络开发工具选项卡的“已转移”列中看到在远程服务 https://jsonplaceholder.typicode.com/users
上调用了大量 GET 并带有 NS_BINDING_ABORTED
消息。
编辑 1 来自 http 调用的流不提供任何数据(控制台日志中没有以“Response:”开头的条目。因此,构造 http 调用后的管道不存在问题。< /p>
问题是为什么会发生这种情况?是什么导致 canActivate 方法执行多次?
例如,如果我删除 http 调用并将其保留如下:
@Injectable()
export class VersionGuardService implements CanActivate {
constructor(private router: Router, private http: HttpClient){
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
console.log("In the Guard");
return of(false);
}
}
它按预期工作(Firefox 控制台中只有一个条目“In the Guard”)。
我想使用 Guard 的原因是在网站初始化时检查应用程序版本兼容性(与后端服务),并决定加载登录窗口或重定向到错误消息站点。
编辑 2 如果我将守卫移动到 Routes 中的“稍后”链接(我的意思是:稍后在应用程序中执行的链接)并且守卫按预期工作。所以我认为这是应用程序初始化的问题,所以我在第一个链接上准备了带有守卫的 stackblitz: https://stackblitz.com/edit/angular-ivy-qgfdk3 它也按预期工作。
在此先感谢您的帮助!
答案 0 :(得分:0)
经过几个小时的代码检查后,发现在代码的其他地方定义了一个 HTTP 拦截器。棘手的部分是,如果没有身份验证令牌,Http 拦截器会重定向到“/login”。因此,在我在“/login”路由上添加了带有 http 调用的 CanActivate 防护(不需要 auth 标头)之后 - 灾难已经准备好了:用户 -> /login -> CanActivate -> http call -> /login(因为没有 auth header) -> CanActivate -> http call -> /login 等循环到浏览器 Too many calls to Location or History APIs within a short timeframe
使 HTTP 拦截器有条件地工作后,解决方案开始按预期工作。
答案 1 :(得分:-1)
请通过添加 catchError
来捕获错误并尝试以下代码,
export class VersionGuardService implements CanActivate {
constructor(private router: Router, private http: HttpClient) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.http.request<any>('get', `https://jsonplaceholder.typicode.com/users`).pipe(
map(resp => resp === false ? this.router.navigate(['/']) : !!resp)
catchError(() => this.router.navigate(['/'])),
);
}
}
或者你也可以这样试试,
export class VersionGuardService implements CanActivate {
constructor(private router: Router, private http: HttpClient) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return new Promise(res => {
this.http.request<any>('get', `https://jsonplaceholder.typicode.com/users`).subscribe(
(resp) => {
if (resp === true) {
res(true);
}
this.router.navigate(['/']);
res(false);
},
(error) => {
console.log(error)
this.router.navigate(['/']);
res(false);
}
);
});
}
}