Angular 2 - Routage - CanActivate fonctionne avec Observable


94

J'ai un AuthGuard (utilisé pour le routage) qui implémente CanActivate .

canActivate() {
    return this.loginService.isLoggedIn();
}

Mon problème est que le résultat CanActivate dépend d'un http-get-result - le LoginService renvoie un observable .

isLoggedIn():Observable<boolean> {
    return this.http.get(ApiResources.LOGON).map(response => response.ok);
}

Comment puis-je les réunir - faire dépendre CanActivate d'un état backend?

# # # # # #

EDIT: Veuillez noter que cette question date de 2016 - un stade très précoce d'angular / router a été utilisé.

# # # # # #


2
Avez-vous lu ici? angular.io/docs/ts/latest/guide/router.html rechercher Route Guards Voici la référence API pour CanActivate: angular.io/docs/ts/latest/api/router/index/… comme vous le voyez, il peut retourner soit booléen ou observable <boolean>
mollwe

4
canActivate()peut renvoyer un Observable, assurez-vous simplement que le Observablea terminé (ie. observer.complete()).
Philip Bulley

1
@PhilipBulley et si l'observable émet plus de valeurs puis se termine? Que fait le gardien? Ce que j'ai vu jusqu'à présent, c'est l'utilisation de l' take(1)opérateur Rx pour atteindre la totalité du flux, que faire si j'oublie de l'ajouter?
Felix

Réponses:


146

Vous devez mettre à jour "@ angular / router" vers la dernière version. par exemple "3.0.0-alpha.8"

modifier AuthGuard.ts

@Injectable()
export class AuthGuard implements CanActivate {
    constructor(private loginService:LoginService, private router:Router) { }

    canActivate(next:ActivatedRouteSnapshot, state:RouterStateSnapshot) {
        return this.loginService.isLoggedIn().map(e => {
            if (e) {
                return true;
            }
        }).catch(() => {
            this.router.navigate(['/login']);
            return Observable.of(false);
        });
    }   
}

Si vous avez des questions, demandez moi!


3
Il convient de souligner que cela fonctionne avec les promesses d'une manière très similaire. Pour ma mise en œuvre, en supposant que isLoggedIn()c'est un Promise, vous pouvez le faire isLoggedIn().then((e) => { if (e) { return true; } }).catch(() => { return false; });J'espère que cela aidera les futurs voyageurs!
kbpontius

6
Je devais ajouterimport 'rxjs/add/observable/of';
marc_aragones

1
pas une bonne réponse maintenant IMO .. il ne fournit aucun détail de ce qui se passe côté serveur ... il est dépassé chez alpha! ... il ne suit pas cette meilleure pratique .. angular.io/docs/ts/latest/guide/… .. voir ma réponse mise à jour ci-dessous (j'espère un jour ci-dessus)
danday74

1
canActivate devrait relancer Observable <boolean>
Yoav Schniederman

Great Help :) Merci
gschambial

57

Mise à jour de la réponse de Kery Hu pour Angular 5 et RxJS 5.5 où l' catchopérateur est déconseillé. Vous devez maintenant utiliser l'opérateur catchError conjointement avec les opérateurs pipe et lettable .

import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { catchError, map} from 'rxjs/operators';
import { of } from 'rxjs/observable/of';

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private loginService: LoginService, private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>  {
    return this.loginService.isLoggedIn().pipe(
      map(e => {
        if (e) {
          return true;
        } else {
          ...
        }
      }),
      catchError((err) => {
        this.router.navigate(['/login']);
        return of(false);
      })
    );
  }   

}

J'ai 1 autre appel XHR après isLoggedIn (), et le résultat de XHR est utilisé dans le 2ème appel XHR. Comment avoir un 2ème appel ajax qui acceptera le 1er résultat? L'exemple que vous avez donné est assez simple, pouvez-vous me dire comment utiliser pipe () si j'ai un autre ajax aussi.
Pratik

9

canActivate () accepte Observable<boolean>comme valeur renvoyée. Le garde attendra la résolution de l'observable et examinera la valeur. Si «vrai», il passera la vérification, sinon (toute autre donnée ou erreur générée) rejettera l'itinéraire.

Vous pouvez utiliser l' .mapopérateur pour transformer le Observable<Response>en Observable<boolean>comme ceci:

canActivate(){
    return this.http.login().map((res: Response)=>{
       if ( res.status === 200 ) return true;
       return false;
    });
}

1
qu'en est-il d'un bloc de capture? le bloc catch est appelé si c'est un droit 401?
danday74

1
En angulaire 4 ne fonctionne pas. Il a besoin d'un endroit pour définir un type générique.
gtzinos

2

Je l'ai fait de cette façon:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.userService.auth(() => this.router.navigate(['/user/sign-in']));}

Comme vous pouvez le voir, j'envoie une fonction de secours à userService.auth que faire si l'appel http échoue.

Et dans userService j'ai:

import 'rxjs/add/observable/of';

auth(fallback): Observable<boolean> {
return this.http.get(environment.API_URL + '/user/profile', { withCredentials: true })
  .map(() => true).catch(() => {
    fallback();
    return Observable.of(false);
  });}

vraiment bonne réponse en termes de fonction .map () - vraiment vraiment bien :) - n'a pas utilisé le rappel de secours - au lieu de cela je me suis abonné à l'auth Observable dans la méthode canActivate - merci beaucoup pour l'idée
danday74

0

Cela peut vous aider

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { Select } from '@ngxs/store';
import { Observable } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { AuthState } from 'src/app/shared/state';

export const ROLE_SUPER = 'ROLE_SUPER';

@Injectable()
export class AdminGuard implements CanActivate {

 @Select(AuthState.userRole)
  private userRoles$: Observable<string[]>;

  constructor(private router: Router) {}

 /**
   * @description Checks the user role and navigate based on it
   */

 canActivate(): Observable<boolean> {
    return this.userRoles$.pipe(
      take(1),
      map(userRole => {
        console.log(userRole);
        if (!userRole) {
          return false;
        }
        if (userRole.indexOf(ROLE_SUPER) > -1) {
          return true;
        } else {
          this.router.navigate(['/login']);
        }
        return false;
      })
    );
  } // canActivate()
} // class

-1

CanActivate fonctionne avec Observable mais échoue lorsque 2 appels sont effectués comme CanActivate: [Guard1, Guard2].
Ici, si vous retournez un Observable de false de Guard1, il vérifiera également Guard2 et autorisera l'accès à la route si Guard2 renvoie true. Pour éviter cela, Guard1 doit retourner un booléen au lieu de Observable ou boolean.


-10

dans canActivate (), vous pouvez retourner une propriété booléenne locale (par défaut à false dans votre cas).

private _canActivate: boolean = false;
canActivate() {
  return this._canActivate;
}

Et puis dans le résultat du LoginService, vous pouvez modifier la valeur de cette propriété.

//...
this.loginService.login().subscribe(success => this._canActivate = true);

vous êtes un génie, monsieur.
Kien Nguyen Ngoc
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.