Angular 2 - Routing - CanActivate ทำงานกับ Observable


94

ฉันมีAuthGuard (ใช้สำหรับการกำหนดเส้นทาง) ที่ดำเนินการCanActivate

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

ปัญหาของฉันคือว่า CanActivate ผลขึ้นอยู่กับ http-ได้รับผล - The LoginServiceส่งกลับสังเกต

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

ฉันจะนำสิ่งเหล่านี้มารวมกัน - ทำให้ CanActivate ขึ้นอยู่กับสถานะแบ็กเอนด์ได้อย่างไร

# # # # # #

แก้ไข: โปรดทราบว่าคำถามนี้มาจากปี 2559 - มีการใช้เชิงมุม / เราเตอร์ในช่วงแรก ๆ

# # # # # #


2
คุณอ่านที่นี่แล้วหรือยัง? angular.io/docs/ts/latest/guide/router.htmlค้นหา Route Guards นี่คือการอ้างอิง api สำหรับ CanActivate: angular.io/docs/ts/latest/api/router/index/…ตามที่คุณเห็นสามารถส่งคืนได้เช่นกัน บูลีนหรือสังเกตได้ <boolean>
mollwe

4
canActivate()สามารถส่งคืนได้Observableเพียงตรวจสอบให้แน่ใจว่าObservableเสร็จสมบูรณ์แล้ว (เช่น. observer.complete())
Philip Bulley

1
@PhilipBulley จะเกิดอะไรขึ้นถ้าสิ่งที่สังเกตได้ปล่อยค่าออกมามากขึ้นแล้วเสร็จสมบูรณ์? ยามทำอะไร? สิ่งที่ฉันได้เห็นจนถึงตอนนี้คือการใช้take(1)ตัวดำเนินการ Rx เพื่อให้สตรีมสมบูรณ์จะเกิดอะไรขึ้นถ้าฉันลืมเพิ่ม
Felix

คำตอบ:


146

คุณควรอัปเกรด "@ angular / router" เป็นรุ่นล่าสุด เช่น "3.0.0-alpha.8"

แก้ไข 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);
        });
    }   
}

หากคุณมีคำถามใด ๆ ถามฉัน!


3
มันคุ้มที่จะชี้ให้เห็นว่าสิ่งนี้ใช้ได้ผลกับคำสัญญาในลักษณะที่คล้ายกันมาก สำหรับการดำเนินงานของฉันสมมติว่าisLoggedIn()เป็นPromiseคุณสามารถทำisLoggedIn().then((e) => { if (e) { return true; } }).catch(() => { return false; });หวังว่านี้จะช่วยให้นักเดินทางในอนาคต!
kbpontius

6
ฉันต้องเพิ่มimport 'rxjs/add/observable/of';
marc_aragones

1
ไม่ใช่คำตอบที่ดีตอนนี้ IMO .. ไม่มีรายละเอียดว่ากำลังเกิดอะไรขึ้นฝั่งเซิร์ฟเวอร์ ... มันล้าสมัยที่ alpha! ... มันไม่เป็นไปตามแนวทางปฏิบัติที่ดีที่สุดนี้ .. angular.io/docs/ts/latest/guide/… .. ดูคำตอบที่อัปเดตของฉันด้านล่าง (หวังว่าหนึ่งวันข้างบน)
danday74

1
canActivate ควรเรียกใช้ Observable <boolean> อีกครั้ง
Yoav Schniederman

ความช่วยเหลือที่ดี :) ขอบคุณ
gschambial

57

การอัปเดตคำตอบของ Kery Hu สำหรับ Angular 5 และ RxJS 5.5 ที่ตัวcatchดำเนินการเลิกใช้งาน ตอนนี้คุณควรจะใช้ประกอบการ catchError ร่วมกับท่อและผู้ประกอบการโรงงานให้เช่า

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

}

ฉันมีการโทร XHR อีก 1 ครั้งหลังจาก isLoggedIn () และผลลัพธ์ของ XHR ถูกใช้ในการโทร XHR ครั้งที่ 2 วิธีการเรียก ajax ครั้งที่ 2 ซึ่งจะรับผลที่ 1 ได้อย่างไร? ตัวอย่างที่คุณให้มานั้นค่อนข้างง่ายโปรดแจ้งให้เราทราบวิธีใช้ pipe () ได้ไหมถ้าฉันมี ajax อื่นด้วย
Pratik

9

canActivate ()ยอมรับObservable<boolean>เป็นค่าที่ส่งคืน ผู้คุมจะรอให้ Observable แก้ไขและดูค่า หาก 'เป็นจริง' จะผ่านการตรวจสอบมิฉะนั้น (ข้อมูลอื่น ๆ หรือข้อผิดพลาดที่เกิดขึ้น) จะปฏิเสธเส้นทาง

คุณสามารถใช้.mapประกอบการที่จะเปลี่ยนObservable<Response>ไปObservable<boolean>เช่นดังนั้น:

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

1
แล้ว Catch Block ล่ะ? Catch block เรียกว่าถ้าเป็น 401 ใช่ไหม
danday74

1
ในเชิงมุม 4 ไม่ทำงาน ต้องมีที่ไหนสักแห่งเพื่อกำหนดประเภททั่วไป
gtzinos

2

ฉันได้ทำในลักษณะนี้:

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

อย่างที่คุณเห็นฉันกำลังส่งฟังก์ชันทางเลือกไปยัง userService.auth จะทำอย่างไรหากการโทร http ล้มเหลว

และใน userService ฉันมี:

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

คำตอบที่ดีจริงๆในแง่ของฟังก์ชั่น. map () - ดีจริงๆ :) - ไม่ได้ใช้การเรียกกลับทางเลือก - ฉันสมัครใช้งาน auth Observable ในวิธี canActivate แทน - ขอบคุณมากสำหรับแนวคิด
danday74

0

สิ่งนี้อาจช่วยคุณได้

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 ทำงานร่วมกับ Observable แต่ล้มเหลวเมื่อมีการเรียก 2 ครั้งเช่น CanActivate: [Guard1, Guard2]
ที่นี่หากคุณคืนค่า Observable of false จาก Guard1 มันก็จะตรวจสอบใน Guard2 และอนุญาตให้เข้าถึงเส้นทางหาก Guard2 ส่งคืนจริง เพื่อหลีกเลี่ยงสิ่งนั้น Guard1 ควรส่งคืนบูลีนแทนที่จะเป็น Observable ของบูลีน


-10

ใน canActivate () คุณสามารถส่งคืนคุณสมบัติบูลีนในเครื่อง (ค่าเริ่มต้นเป็นเท็จในกรณีของคุณ)

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

จากนั้นในผลลัพธ์ของ LoginService คุณสามารถแก้ไขค่าของคุณสมบัตินั้นได้

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

คุณเป็นอัจฉริยะครับ
Kien Nguyen Ngoc
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.