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

Posted

技术标签:

【中文标题】Angular 2 - 路由 - CanActivate 与 Observable 一起工作【英文标题】:Angular 2 - Routing - CanActivate work with Observable 【发布时间】:2016-10-23 05:12:35 【问题描述】:

我有一个实现 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 年开始的 - 角度/路由器的早期阶段已经被使用。

######

【问题讨论】:

你读过这里吗? angular.io/docs/ts/latest/guide/router.html 搜索 Route Guards 这是 CanActivate 的 api 参考:angular.io/docs/ts/latest/api/router/index/… 如您所见,它可以返回布尔值或 Observable canActivate() 可以返回一个Observable,只要确保Observable 已经完成(即observer.complete())。 @PhilipBulley 如果 observable 发出更多值然后完成怎么办?守门员是做什么的?目前我看到的是使用take(1)Rx操作符来实现stream的completnes,如果忘记添加怎么办? 【参考方案1】:

在 canActivate() 中,您可以返回一个本地布尔属性(在您的情况下默认为 false)。

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

然后在LoginService的结果中,你可以修改那个属性的值。

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

【讨论】:

你是个天才,先生。【参考方案2】:

您应该将“@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);
            );
    

如果你有任何问题,可以问我!

【讨论】:

值得指出的是,这与 Promise 的工作方式非常相似。对于我的实现,假设isLoggedIn()Promise,您可以使用isLoggedIn().then((e) =&gt; if (e) return true; ).catch(() =&gt; return false; ); 希望这对未来的旅行者有所帮助! 我必须添加import 'rxjs/add/observable/of'; 现在不是一个好的答案 IMO .. 它没有提供服务器端正在发生的事情的详细信息......它在 alpha 时已经过时了! ...它不遵循此最佳实践..angular.io/docs/ts/latest/guide/… ..请参阅下面的更新答案(希望一天以上) canActivate 应该重新调整 Observable 现在是import Observable, of from 'rxjs';【参考方案3】:

我是这样做的:

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 - 非常感谢想法【参考方案4】:

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

您可以使用.map 运算符将Observable&lt;Response&gt; 转换为Observable&lt;boolean&gt;,如下所示:

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

【讨论】:

catch 块怎么样?如果 401 正确,则调用 catch 块? 在角度 4 中不起作用。它需要在某个地方定义一个泛型类型。【参考方案5】:

CanActivate 确实可以与 Observable 一起使用,但在进行 2 次调用时失败,例如 CanActivate:[Guard1, Guard2]。 在这里,如果您从 Guard1 返回一个 false 的 Observable,那么它也会检查 Guard2 并在 Guard2 返回 true 时允许访问路由。 为了避免这种情况,Guard1 应该返回一个布尔值而不是布尔值的 Observable。

【讨论】:

【参考方案6】:

更新 Kery Hu 对 Angular 5+ 和 RxJS 5.5 where the catch operator is deprecated. You should now use the catchError operator in conjunction with pipe 和 lettable operators 的回答。

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()。 对于 angular 10 也非常有用 谢谢。你救了我的一天!对 Angular 11 有用。【参考方案7】:

这可能对你有帮助

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

【讨论】:

以上是关于Angular 2 - 路由 - CanActivate 与 Observable 一起工作的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2 - 如何使用新的 Angular 2.0.0-rc.1 路由器

Angular 2 可选路由参数

延迟加载到Angular 2中的空路由以外的路由

Angular 2 的异步加载路由数据和构建路由指令

在 Angular 2 中,如何使用父路由确定子路由中的活动路由?

Angular 2 路由器事件监听器