CanActivate 守卫导致多次 http 调用

时间:2021-07-01 15:12:29

标签: angular canactivate

我观察到我的 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 它也按预期工作。

在此先感谢您的帮助!

2 个答案:

答案 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);
    }
  );
});

 }
}