Winkel 2 - Routing - Kann die Arbeit mit Observable aktivieren

92

Ich habe einen AuthGuard (für das Routing verwendet), der CanActivate implementiert .

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

Mein Problem ist, dass das CanActivate-Ergebnis von einem http-get-Ergebnis abhängt - der LoginService gibt ein Observable zurück .

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

Wie kann ich diese zusammenbringen - damit CanActivate von einem Backend-Status abhängt?

# # # # # #

EDIT: Bitte beachten Sie, dass diese Frage aus dem Jahr 2016 stammt - ein sehr frühes Stadium von Angular / Router wurde verwendet.

# # # # # #

Philipp
quelle
2
Hast du hier gelesen angular.io/docs/ts/latest/guide/router.html Suche nach Routenwächtern Hier ist eine API-Referenz für CanActivate: angular.io/docs/ts/latest/api/router/index/…, wie Sie sehen, kann es auch zurückkehren Boolean oder Observable <Boolean>
Mollwe
4
canActivate()kann ein zurückgeben Observable, stellen Sie einfach sicher, dass das Observableabgeschlossen ist (dh. observer.complete()).
Philip Bulley
1
@PhilipBulley Was ist, wenn das Observable mehr Werte ausgibt und dann abgeschlossen wird? Was macht die Wache? Was ich bisher gesehen habe, ist die Verwendung des take(1)Rx-Operators, um die Vollständigkeit des Streams zu erreichen. Was ist, wenn ich vergesse, ihn hinzuzufügen?
Felix

Antworten:

142

Sie sollten "@ angle / router" auf den neuesten Stand bringen. zB "3.0.0-alpha.8"

Ändern Sie 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);
        });
    }   
}

Wenn Sie Fragen haben, fragen Sie mich!

Kery Hu
quelle
3
Es sei darauf hingewiesen, dass dies mit Versprechungen auf sehr ähnliche Weise funktioniert. Unter der Annahme, dass dies isLoggedIn()eine ist Promise, können Sie dies tun. isLoggedIn().then((e) => { if (e) { return true; } }).catch(() => { return false; });Hoffentlich hilft dies zukünftigen Reisenden!
Kbpontius
6
Ich musste hinzufügenimport 'rxjs/add/observable/of';
marc_aragones
1
Keine gute Antwort jetzt IMO .. es liefert keine Details darüber, was auf der Serverseite passiert ... es ist bei Alpha veraltet! ... es folgt nicht dieser Best Practice .. angle.io/docs/ts/latest/guide/… .. siehe meine aktualisierte Antwort unten (hoffentlich einen Tag oben)
danday74
1
canActivate sollte Observable <boolean>
Yoav Schniederman
Große Hilfe :) Danke
gschambial
55

Aktualisierung der Antwort von Kery Hu für Angular 5 und RxJS 5.5, bei denen der catchBediener veraltet ist. Sie sollten jetzt den Operator catchError in Verbindung mit Operatoren für Pipe und Vermietung verwenden .

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

}
Derek Hill
quelle
Ich habe 1 weiteren XHR-Aufruf nach isLoggedIn () und das Ergebnis von XHR wird im 2. XHR-Aufruf verwendet. Wie bekomme ich einen 2. Ajax-Anruf, der für das 1. Ergebnis akzeptiert wird? Das Beispiel, das Sie gegeben haben, ist ziemlich einfach. Können Sie mir bitte mitteilen, wie man pipe () verwendet, wenn ich auch einen anderen Ajax habe?
Pratik
9

canActivate () akzeptiert Observable<boolean>als zurückgegebenen Wert. Der Wachmann wartet, bis sich das Observable aufgelöst hat, und überprüft den Wert. Wenn 'true', besteht es die Prüfung, andernfalls (andere Daten oder ausgelöste Fehler) wird die Route abgelehnt.

Sie können die Verwendung .mapOperator der Transformation Observable<Response>zu Observable<boolean>etwa so:

canActivate(){
    return this.http.login().map((res: Response)=>{
       if ( res.status === 200 ) return true;
       return false;
    });
}
mp3por
quelle
1
Was ist mit einem Fangblock? Der Catch-Block wird aufgerufen, wenn es ein 401 ist, oder?
danday74
1
In Winkel 4 funktioniert nicht. Es muss irgendwo ein generischer Typ definiert werden.
Gtzinos
2

Ich habe es so gemacht:

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

Wie Sie sehen, sende ich eine Fallback-Funktion an userService.auth, was zu tun ist, wenn der http-Aufruf fehlschlägt.

Und in userService habe ich:

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);
  });}
Karolis Grinkevičius
quelle
wirklich gute Antwort in Bezug auf die .map () -Funktion - wirklich sehr gut :) - habe den Fallback-Rückruf nicht verwendet - stattdessen habe ich die auth Observable in der canActivate-Methode abonniert - vielen Dank für die Idee
danday74
0

Dies kann Ihnen helfen

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
Kalanka
quelle
-2

CanActivate funktioniert mit Observable, schlägt jedoch fehl, wenn zwei Aufrufe wie CanActivate getätigt werden: [Guard1, Guard2].
Wenn Sie hier ein Observable of false von Guard1 zurückgeben, wird auch Guard2 eingecheckt und der Zugriff auf die Route ermöglicht, wenn Guard2 true zurückgibt. Um dies zu vermeiden, sollte Guard1 einen Booleschen Wert anstelle von Observable of Boolean zurückgeben.

Arjunsingh
quelle
-10

In canActivate () können Sie eine lokale boolesche Eigenschaft zurückgeben (in Ihrem Fall standardmäßig false).

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

Im Ergebnis des LoginService können Sie den Wert dieser Eigenschaft ändern.

//...
this.loginService.login().subscribe(success => this._canActivate = true);
Nguyễn Việt Trung
quelle
Sie sind ein Genie, Sir.
Kien Nguyen Ngoc