Angular 2 - Маршрутизація - CanActivate роботу з Observable


94

У мене є AuthGuard (використовується для маршрутизації), який реалізує CanActivate .

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

Моя проблема полягає в тому, що результат CanActivate залежить від http-get-result - LoginService повертає Observable .

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

Як я можу їх об’єднати - зробити CanActivate залежним від серверного стану?

# # # # # #

EDIT: Будь ласка, зверніть увагу, що це питання з 2016 року - був використаний дуже ранній етап кутового / маршрутизатора.

# # # # # #


2
Ви читали тут? angular.io/docs/ts/latest/guide/router.html пошук охоронців маршрутів Ось посилання на api для CanActivate: angular.io/docs/ts/latest/api/router/index/… як ви бачите, він може повернути або boolean або Observable <boolean>
mollwe

4
canActivate()може повернути Observable, просто переконайтесь, що Observableфайл завершено (тобто. observer.complete()).
Філіп Буллі

1
@PhilipBulley, що якщо спостережуване видає більше значень, а потім завершує? Що робить охоронець? Те, що я бачив досі, це використання take(1)оператора Rx для досягнення завершеності потоку. Що робити, якщо я забуду додати його?
Фелікс

Відповіді:


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

Якщо у вас є питання, задайте мені ask


3
Варто зазначити, що це працює з обіцянками дуже подібним чином. Для мого впровадження, припускаючи, що isLoggedIn()це Promise, ви можете зробити isLoggedIn().then((e) => { if (e) { return true; } }).catch(() => { return false; });Сподіваємось, це допомагає майбутнім мандрівникам!
kbpontius

6
Мені довелося додатиimport 'rxjs/add/observable/of';
marc_aragones

1
не гарна відповідь зараз IMO .. він не надає жодних деталей того, що відбувається на стороні сервера ... він застарів на альфа-версії! ... він не слідує цій найкращій практиці .. angular.io/docs/ts/latest/guide/… .. див. мою оновлену відповідь нижче (сподіваюся, один день вище)
danday74,

1
canActivate повинен переналаштувати Observable <boolean>
Йоав Шнідерман

Велика допомога :) Дякую
gschambial

57

Оновлення відповіді Кері Ху для Angular 5 та RxJS 5.5, де catchоператор застарілий. Тепер ви повинні використовувати оператор catchError у поєднанні з операторами pipe і letable .

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

}

У мене є ще 1 виклик XHR після isLoggedIn (), і результат XHR використовується у другому виклику XHR. Як мати 2-й дзвінок ajax, який прийме за 1-й результат? Наведений вами приклад досить простий, можете, будь ласка, дайте мені знати, як використовувати pipe (), якщо у мене теж є інший ajax.
Pratik

9

canActivate () приймає Observable<boolean>як повернене значення. Охоронець буде чекати, поки спостережливий вирішить, і подивиться на значення. Якщо значення "true", воно пройде перевірку, інакше (будь-які інші дані або помилка) буде відхилено маршрут.

Ви можете використовувати .mapоператор, щоб перетворити його Observable<Response>на Observable<boolean>вподобання:

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

1
а як щодо блоку catch? викликається блок catch, якщо його 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 false з Guard1, тоді він також перевірить Guard2 і надасть доступ до маршруту, якщо Guard2 поверне true. Щоб цього уникнути, Guard1 повинен повернути логічне значення замість Observable з логічного значення.


-10

у canActivate () ви можете повернути локальну логічну властивість (у вашому випадку за замовчуванням значення false).

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

А потім у результаті LoginService ви можете змінити значення цього властивості.

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

ви геній, сер.
Kien Nguyen Ngoc
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.