網站搜尋

typescript - Angular2 canActivate() 呼叫異步函數


我正在嘗試使用 Angular2 路由守衛來限制對我應用程序中某些頁面的訪問。我正在使用 Firebase 身份驗證。為了檢查用戶是否已使用 Firebase 登錄,我必須對 FirebaseAuth 對象調用 .subscribe() 並傳入回調函數。以下是守衛的代碼:

import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AngularFireAuth } from "angularfire2/angularfire2";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Rx";

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(private auth: AngularFireAuth, private router: Router) {}

    canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>|boolean {
        this.auth.subscribe((auth) => {
            if (auth) {
                console.log('authenticated');
                return true;
            }
            console.log('not authenticated');
            this.router.navigateByUrl('/login');
            return false;
        });
    }
}

當我導航到有守衛的頁面時,控制台會打印出 authenticated 或 not authenticated(在等待 Firebase 回應之後會有一些延遲)。然而,導航從未完成。此外,如果我沒有登入,我會被重定向到 /login 路由。所以,我遇到的問題是 return true 並沒有將請求的頁面展示給使用者。我猜這是因為我使用了回調函數,但我無法找出其他方式該怎麼做。有什麼建議嗎?

答案#1

canActivate 需要返回一個會完成的 Observable:

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(private auth: AngularFireAuth, private router: Router) {}

    canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>|boolean {
        return this.auth.map((auth) => {
            if (auth) {
                console.log('authenticated');
                return true;
            }
            console.log('not authenticated');
            this.router.navigateByUrl('/login');
            return false;
        }).first(); // this might not be necessary - ensure `first` is imported if you use it
    }
}

缺少一個 return,我使用 map() 而不是 subscribe(),因為 subscribe() 返回的是 Subscription 而不是 Observable

答案#2

你可能會使用 Observable 來處理非同步邏輯部分。這裡是我測試的範例程式碼:

import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { DetailService } from './detail.service';

@Injectable()
export class DetailGuard implements CanActivate {

  constructor(
    private detailService: DetailService
  ) {}

  public canActivate(): boolean|Observable<boolean> {
    if (this.detailService.tempData) {
      return true;
    } else {
      console.log('loading...');
      return new Observable<boolean>((observer) => {
        setTimeout(() => {
          console.log('done!');
          this.detailService.tempData = [1, 2, 3];
          observer.next(true);
          observer.complete();
        }, 1000 * 5);
      });
    }
  }
}

答案#3

canActivate 也可以返回一個解析為 boolean 的 Promise

答案#4

你可以以承諾的形式返回 true|false。

import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {Observable} from 'rxjs';
import {AuthService} from "../services/authorization.service";

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

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
  return new Promise((resolve, reject) => {
  this.authService.getAccessRights().then((response) => {
    let result = <any>response;
    let url = state.url.substr(1,state.url.length);
    if(url == 'getDepartment'){
      if(result.getDepartment){
        resolve(true);
      } else {
        this.router.navigate(['login']);
        resolve(false);
      }
    }

     })
   })
  }
}