Angular 2 - Routing - CanActivate làm việc với Observable


94

Tôi có AuthGuard (được sử dụng để định tuyến) triển khai CanActivate .

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

Vấn đề của tôi là, kết quả CanActivate phụ thuộc vào một http-get-result - LoginService trả về một Observable .

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

Làm cách nào để kết hợp chúng lại với nhau - làm cho CanActivate phụ thuộc vào trạng thái phụ trợ?

# # # # # #

CHỈNH SỬA: Xin lưu ý rằng câu hỏi này là từ năm 2016 - giai đoạn đầu của thiết bị định tuyến / góc cạnh đã được sử dụng.

# # # # # #


2
Bạn đã đọc ở đây chưa? angle.io/docs/ts/latest/guide/router.html search for Route Guards Đây là tài liệu tham khảo api cho CanActivate: angle.io/docs/ts/latest/api/router/index/… khi bạn thấy nó có thể trả về boolean hoặc Quan sát <boolean>
mollwe

4
canActivate()có thể trả về một Observable, chỉ cần đảm bảo rằng Observableđã hoàn thành (tức là. observer.complete()).
Philip Bulley

1
@PhilipBulley điều gì sẽ xảy ra nếu có thể quan sát phát ra nhiều giá trị hơn và sau đó hoàn thành? Người bảo vệ làm gì? Những gì tôi đã thấy cho đến nay là sử dụng take(1)toán tử Rx để đạt được hoàn thành của luồng, Điều gì sẽ xảy ra nếu tôi quên thêm nó?
Felix

Câu trả lời:


146

Bạn nên nâng cấp "@ angle / router" lên phiên bản mới nhất. ví dụ: "3.0.0-alpha.8"

sửa đổi 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);
        });
    }   
}

Nếu bạn có bất kỳ câu hỏi nào, hãy hỏi tôi!


3
Cần chỉ ra rằng điều này hoạt động với những lời hứa theo một cách rất giống nhau. Đối với cách triển khai của tôi, giả sử isLoggedIn()là a Promise, bạn có thể làm được. isLoggedIn().then((e) => { if (e) { return true; } }).catch(() => { return false; });Hy vọng rằng điều này sẽ giúp ích cho những khách du lịch trong tương lai!
kbpontius

6
Tôi đã có thêmimport 'rxjs/add/observable/of';
marc_aragones

1
không phải là một câu trả lời tốt bây giờ IMO .. nó không cung cấp chi tiết về những gì đang xảy ra phía máy chủ ... nó đã lỗi thời ở giai đoạn alpha! ... nó không tuân theo phương pháp hay nhất này .. angle.io/docs/ts/latest/guide/… .. xem câu trả lời được cập nhật của tôi bên dưới (hy vọng một ngày ở trên)
danday74

1
canActivate nên retun Quan sát <boolean>
Yoav Schniederman

Lớn giúp :) Cảm ơn
gschambial

57

Cập nhật câu trả lời của Kery Hu cho Angular 5 và RxJS 5.5 trong đó catchtoán tử không được chấp nhận. Bây giờ bạn nên sử dụng toán tử catchError kết hợp với các toán tử pipelettable .

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

}

Tôi có thêm 1 lệnh gọi XHR sau isLoggedIn () và kết quả của XHR được sử dụng trong lệnh gọi XHR thứ 2. Làm thế nào để có cuộc gọi ajax thứ 2 sẽ chấp nhận kết quả đầu tiên? Ví dụ bạn đưa ra khá dễ dàng, bạn có thể cho tôi biết cách sử dụng pipe () nếu tôi cũng có một ajax khác.
Pratik

9

canActivate () chấp nhận Observable<boolean>là giá trị trả về. Người bảo vệ sẽ đợi Observable giải quyết và xem xét giá trị. Nếu 'true', nó sẽ vượt qua kiểm tra, còn lại (bất kỳ dữ liệu nào khác hoặc lỗi được đưa ra) sẽ từ chối tuyến đường.

Bạn có thể sử dụng .maptoán tử để biến đổi Observable<Response>thành Observable<boolean>như vậy:

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

1
những gì về một khối bắt? khối catch được gọi nếu nó là 401 phải không?
danday74 Ngày

1
Trong góc 4 không hoạt động. Nó cần một nơi nào đó để xác định một kiểu chung.
gtzinos

2

Tôi đã làm theo cách này:

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

Như bạn có thể thấy, tôi đang gửi một hàm dự phòng tới userService.auth phải làm gì nếu cuộc gọi http không thành công.

Và trong userService tôi có:

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

Câu trả lời thực sự tốt trong điều khoản của .map () chức năng - thực sự thực sự tốt :) - không sử dụng callback fallback - thay vào đó tôi đăng ký với auth Quan sát trong phương pháp canActivate - cảm ơn rất nhiều cho ý tưởng
danday74

0

Điều này có thể giúp bạn

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 hoạt động với Observable nhưng không thành công khi 2 cuộc gọi được thực hiện như CanActivate: [Guard1, Guard2].
Ở đây nếu bạn trả về một Observable là false từ Guard1 thì nó cũng sẽ kiểm tra trong Guard2 và cho phép truy cập định tuyến nếu Guard2 trả về true. Để tránh điều đó, Guard1 nên trả về một boolean thay vì Observable của boolean.


-10

trong canActivate (), bạn có thể trả về một thuộc tính boolean cục bộ (mặc định là false trong trường hợp của bạn).

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

Và sau đó trong kết quả của LoginService, bạn có thể sửa đổi giá trị của thuộc tính đó.

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

bạn là một thiên tài, thưa ông.
Kien Nguyen Ngoc
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.