Angular 2 - Routing - CanActivate bekerja dengan Observable

94

Saya memiliki AuthGuard (digunakan untuk perutean) yang mengimplementasikan CanActivate .

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

Masalah saya adalah, bahwa CanActivate-result bergantung pada http-get-result - LoginService mengembalikan sebuah Observable .

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

Bagaimana cara menyatukannya - membuat CanActivate bergantung pada status backend?

# # # # # #

EDIT: Harap dicatat, bahwa pertanyaan ini dari 2016 - tahap awal sudut / router telah digunakan.

# # # # # #

Philipp
sumber
2
Sudahkah kamu membaca di sini? angular.io/docs/ts/latest/guide/router.html search for Route Guards Berikut adalah referensi api untuk CanActivate: angular.io/docs/ts/latest/api/router/index/… seperti yang Anda lihat dapat kembali boolean atau Observable <boolean>
mollwe
4
canActivate()dapat mengembalikan Observable, pastikan saja bahwa Observabletelah selesai (mis. observer.complete()).
Philip Bulley
1
@PhilipBulley bagaimana jika yang dapat diamati memancarkan lebih banyak nilai lalu selesai? Apa yang dilakukan penjaga itu? Apa yang saya lihat sejauh ini adalah penggunaan take(1)operator Rx untuk mencapai kelengkapan streaming, Bagaimana jika saya lupa menambahkannya?
Felix

Jawaban:

146

Anda harus meningkatkan "@ angular / router" ke yang terbaru. misalnya "3.0.0-alpha.8"

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

Jika Anda memiliki pertanyaan, tanyakan kepada saya!

Kery Hu
sumber
3
Perlu diperhatikan bahwa ini bekerja dengan promise dengan cara yang sangat mirip. Untuk implementasi saya, dengan asumsi isLoggedIn()adalah Promise, Anda bisa melakukan isLoggedIn().then((e) => { if (e) { return true; } }).catch(() => { return false; });Semoga ini membantu pelancong masa depan!
kbpontius
6
Saya harus menambahkanimport 'rxjs/add/observable/of';
marc_aragones
1
bukan jawaban yang bagus sekarang IMO .. ini tidak memberikan detail tentang apa yang terjadi di sisi server ... itu sudah ketinggalan zaman di alpha! ... tidak mengikuti praktik terbaik ini .. angular.io/docs/ts/latest/guide/… .. lihat jawaban terbaru saya di bawah (mudah-mudahan satu hari di atas)
danday74
1
canActivate harus menjalankan kembali Observable <boolean>
Yoav Schniederman
Great Help :) Terima kasih
gschambial
57

Memperbarui jawaban Kery Hu untuk Angular 5 dan RxJS 5.5 di mana catchoperator tidak digunakan lagi. Sekarang Anda harus menggunakan operator catchError bersama dengan operator pipa dan 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);
      })
    );
  }   

}
Derek Hill
sumber
Saya memiliki 1 panggilan XHR lagi setelah isLoggedIn (), dan hasil XHR digunakan dalam panggilan XHR ke-2. Bagaimana cara melakukan panggilan ajax ke-2 yang akan diterima untuk hasil pertama? Contoh yang Anda berikan cukup mudah, dapatkah Anda memberi tahu saya cara menggunakan pipe () jika saya juga memiliki ajax lain.
Pratik
9

canActivate () menerima Observable<boolean>sebagai nilai yang dikembalikan. Penjaga akan menunggu Observable untuk menyelesaikan dan melihat nilainya. Jika 'benar' itu akan lolos pemeriksaan, yang lain (data lain atau kesalahan yang dilemparkan) akan menolak rute.

Anda dapat menggunakan .mapoperator untuk mengubah Observable<Response>menjadi Observable<boolean>seperti ini:

canActivate(){
    return this.http.login().map((res: Response)=>{
       if ( res.status === 200 ) return true;
       return false;
    });
}
mp3por
sumber
1
bagaimana dengan blok tangkapan? blok tangkap dipanggil jika itu adalah 401 kan?
danday74
1
Dalam sudut 4 tidak berhasil. Ini membutuhkan tempat untuk mendefinisikan tipe generik.
gtzinos
2

Saya telah melakukannya dengan cara ini:

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

Seperti yang Anda lihat, saya mengirimkan fungsi fallback ke userService.auth apa yang harus dilakukan jika panggilan http gagal.

Dan di userService saya punya:

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
sumber
jawaban yang sangat bagus dalam hal fungsi .map () - benar-benar bagus :) - tidak menggunakan callback fallback - sebagai gantinya saya berlangganan auth Observable dalam metode canActivate - terima kasih banyak atas idenya
danday74
0

Ini dapat membantu Anda

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
sumber
-1

CanActivate berfungsi dengan Observable tetapi gagal ketika 2 panggilan dilakukan seperti CanActivate: [Guard1, Guard2].
Di sini jika Anda mengembalikan Observable of false dari Guard1, maka itu juga akan memeriksa di Guard2 dan mengizinkan akses ke rute jika Guard2 mengembalikan true. Untuk menghindarinya, Guard1 harus menampilkan boolean, bukan Observable of boolean.

Arjunsingh
sumber
-10

di canActivate (), Anda bisa mengembalikan properti boolean lokal (defaultnya ke false dalam kasus Anda).

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

Dan kemudian pada hasil LoginService, Anda bisa mengubah nilai properti itu.

//...
this.loginService.login().subscribe(success => this._canActivate = true);
Nguyễn Việt Trung
sumber
Anda jenius, Pak.
Kien Nguyen Ngoc