Angular 2-路由-CanActivate与Observable一起工作


94

我有一个实现CanActivateAuthGuard(用于路由)。

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年开始的-角/路由器的非常早期阶段已被使用。

######


2
你在这里读过吗?angular.io/docs/ts/latest/guide/router.html搜索路线护卫这是CanActivate的api参考:angular.io/docs/ts/latest/api/router/index/…如您所见,它可以返回boolean或Observable <boolean>
mollwe

4
canActivate()可以返回Observable,只需确保Observable已完成(即observer.complete())。
菲利普·布利2016年

1
@PhilipBulley如果可观察对象发出更多值然后完成,该怎么办?守卫做什么?到目前为止,我所看到的是使用take(1)Rx运算符来实现流的完成,如果我忘记添加它怎么办?
菲利克斯(Felix)

Answers:


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
值得指出的是,这与promise的工作方式非常相似。对于我的实现,假设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,2017年

1
canActivate应该重新调整Observable <boolean>
Yoav Schniederman

极大的帮助:)谢谢
gschambial '17

57

更新柯瑞胡锦涛对角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);
      })
    );
  }   

}

在isLoggedIn()之后,我还有1个XHR调用,并且XHR的结果用于第二个XHR调用。如何进行将要接受第一结果的第二ajax调用?您提供的示例非常简单,如果我也有另一个Ajax,可以让我知道如何使用pipe()吗?
Pratik

9

canActivate()接受Observable<boolean>作为返回值。守卫将等待Observable解析并查看该值。如果为“ true”,它将通过检查,否则(任何其他数据或抛出错误)将拒绝该路由。

您可以使用.map运算符将转换为Observable<Response>Observable<boolean>如下所示:

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

1
捕获块怎么样?如果它的401正确,则调用catch块?
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()函数而言,这确实是个很好的答案-真的非常好:)-没有使用后备回调-而是我在canActivate方法中订阅了auth Observable-非常感谢您的想法
danday74,2017年

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一起使用,但是在进行两次调用(如CanActivate:[Guard1,Guard2])时失败。
在这里,如果您从Guard1返回Observable为false,那么它也会在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);

先生,你真是天才。
肯恩·阮
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.