Angular: RouterGuards, HTTP requests and race conditions

326 Views Asked by At

I'm trying to create a RouterGuard in Angular that gets a response from the backend about the User's login status. The problem is that the HTTP request returns a Subscription with async concrete values. How can I guarantee the Router will "wait" for the http call to be transformed into a real value and then be processed?

AlreadyLoggedRouterGuard:

export class AlreadyAuthIn implements CanActivate {
  constructor(private clientService: ClientService,
              private router: Router) {  }
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

return this.clientService.isUserLogged()
  .subscribe(
    (response: Response) => {
      let loginStatus = response.message;
      if (loginStatus == 'not_logged') {
        localStorage.clear();
        return false;
      } else {
        return true;
      }
    });
}

Note: clientService.isUserLogged() is the http call, and will return a Observable.

This code already shows an Error saying that should not return a Subscription. Then I tried to assign the logic to a variable like:

let logged = this.clientService.isUserLogged().subscribe(...<logic>......)
if (logged) {
  return false;
} else {
  return true;
}

But that creates a new problem: race condition. The If statement is checked before the Subscription turns into a Boolean value, and the program processes the logic wrongly. Maybe we can solve this with a Promise?

1

There are 1 best solutions below

3
Fussel On BEST ANSWER

You are now returning a subscription not the result of your request. Use a map function to turn the return type of your request into an Observable<boolean> which will be used by angular to handle the route guard.

Try something like (untested)

export class AlreadyAuthIn implements CanActivate {
  constructor(private clientService: ClientService,
              private router: Router) {  }
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

return this.clientService.isUserLogged()
  .pipe(map((response: Response) => {
      let loginStatus = response.message;
      if (loginStatus == 'not_logged') {
        localStorage.clear();
        return false;
      } else {
        return true;
      }
    });
}