Angular 2 - Маршрутизация - Может активировать работу с Observable

94

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

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

Моя проблема в том, что CanActivate-result зависит от http-get-result - LoginService возвращает Observable .

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

Как я могу объединить их - сделать CanActivate зависимым от состояния серверной части?

# # # # # #

РЕДАКТИРОВАТЬ: Обратите внимание, что этот вопрос с 2016 года - использовалась очень ранняя стадия angular / router.

# # # # # #

Филипп
источник
2
Вы читали здесь? angular.io/docs/ts/latest/guide/router.html поиск Route Guards Вот ссылка на api для CanActivate: angular.io/docs/ts/latest/api/router/index/… как вы видите, он может вернуть либо boolean or Observable <boolean>
mollwe
4
canActivate()может вернуть Observable, просто убедитесь, что Observableон завершился (т.е. observer.complete()).
Philip Bulley
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);
        });
    }   
}

Если есть вопросы, задавайте!

Кери Ху
источник
3
Стоит отметить, что это работает с обещаниями очень похожим образом. Для моей реализации, предполагая , что isLoggedIn()это Promise, вы можете сделать , isLoggedIn().then((e) => { if (e) { return true; } }).catch(() => { return false; });надеюсь , это поможет будущим путешественникам!
kbpontius
6
Пришлось добавитьimport 'rxjs/add/observable/of';
marc_aragones 07
1
не лучший ответ сейчас ИМО .. он не дает подробностей о том, что происходит на стороне сервера ... он устарел на альфе! ... он не соответствует этой передовой практике .. angular.io/docs/ts/latest/guide/… .. см. мой обновленный ответ ниже (надеюсь, на один день выше)
danday74
1
canActivate должен вернуть Observable <boolean>
Йоав
Отличная помощь :) Спасибо
gschambial
57

Обновление ответа Кери Ху для Angular 5 и RxJS 5.5, где catchоператор устарел. Теперь вы должны использовать оператор catchError вместе с операторами pipe и 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);
      })
    );
  }   

}
Дерек Хилл
источник
У меня есть еще 1 вызов XHR после isLoggedIn (), и результат XHR используется во втором вызове XHR. Как получить 2-й вызов ajax, который будет принимать 1-й результат? Приведенный вами пример довольно прост, пожалуйста, дайте мне знать, как использовать pipe (), если у меня тоже есть другой ajax.
Pratik
9

canActivate () принимает Observable<boolean>как возвращаемое значение. Охранник будет ждать разрешения наблюдаемого и смотреть на значение. Если «истина», он пройдет проверку, иначе (любые другие данные или выданная ошибка) отклонит маршрут.

Вы можете использовать .mapоператор, чтобы преобразовать Observable<Response>его Observable<boolean>так:

canActivate(){
    return this.http.login().map((res: Response)=>{
       if ( res.status === 200 ) return true;
       return false;
    });
}
mp3por
источник
1
как насчет блока захвата? блок catch вызывается, если это 401 правильно?
danday74 09
1
В angular 4 не работает. Необходимо где-то определить универсальный тип.
gtzinos 07
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 of boolean.

Арджунсингх
источник
-10

в canActivate () вы можете вернуть локальное логическое свойство (в вашем случае по умолчанию false).

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

А затем в результате LoginService вы можете изменить значение этого свойства.

//...
this.loginService.login().subscribe(success => this._canActivate = true);
Nguyện Việt Trung
источник
вы гений, сэр.
Киен Нгуен Нгок