首页 文章

如何使用angular调用canActivate中的异步方法?

提问于
浏览
0

我正在尝试在Angular 4.2.4中为管理路由实现canActivate防护 .

基于这个堆栈问题:canActivate Question,我想我似乎可以让事情发挥作用 .

Here is the module guard:

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private _store: Store<AppState>,
              private router: Router,
              private czauth: CzauthService) { }

  canActivate(route: ActivatedRouteSnapshot):Observable<boolean>{
    return this.czauth.verifyAdminUser().first();
  }
};

Here is my czauth service:

getToken():Observable<firebase.User>{
    return this.af.idToken;
  }

  verifyUserRole(token){
      this.token = token;
      console.log(token);// Log out "PromiseObservable {_isScalar: false, promise: D, scheduler: undefined}"
      let headers = new Headers({ 'Authorization': 'Bearer ' + token});
      let options = new RequestOptions({ headers: headers });
      return this.http.get(environment.api+'/verifyadminuser', options).map((response)=>{return response.json()});
  }


  verifyAdminUser():Observable<boolean>{
    return this.getToken()
      .map((user)=>{return Observable.fromPromise(user.getIdToken())})
      .map((token)=>{return this.verifyUserRole(token)})
      .do((response)=>{console.log(response)})// Always returns "Observable {_isScalar: false, source: Observable, operator: MapOperator}"
      .switchMap((response:any)=>{ return response.status === 200 ? Observable.of(true) : Observable.of(false)});
  }

我永远无法从我的http异步调用中获得响应 . 我总是在控制台中看到看起来很冷的东西 . 就好像路由器永远不会订阅我的observable一样?我想根据我的http响应返回一个布尔值 . 我怎样才能做到这一点?

EDIT:

我从守卫那里调用 verifyAdminUser 方法 . 该服务是根模块上的一个sinlgeton . 警卫正在保护对延迟加载模块的访问 .

下面我列出了我在路由中使用防护的地方 .

路由模块:

const routes: Routes = [
  {path: '', loadChildren : './landing/landing.module#LandingModule'},
  {path: 'dashboard', loadChildren : './dashboard/dashboard.module#DashboardModule', canActivate: [ AuthGuard ]}
];

@NgModule({
  imports: [RouterModule.forRoot(routes, {preloadingStrategy: PreloadAllModules})],
  exports: [RouterModule]
})
export class AppRoutingModule { }

问题是当用户尝试导航到仪表板模块时,canActivate总是返回false,因为我的switchMap操作符中的响应未定义 .

EDIT 2:

我在我的代码中重构并简化了以下两种方法,现在一切正常 . 现在,我想了解原因 . 以下是调整方法:

verifyUserRole(){
      let headers = new Headers({ 'Authorization': 'Bearer ' + this.token});
      let options = new RequestOptions({ headers: headers });
      return this.http.get(environment.api+'/verifyadminuser', options).map((response)=>{return response});
  }


  verifyAdminUser():Observable<boolean>{
    return this.getToken()
      .map((user)=>{
        user.getIdToken().then((token)=>this.token = token);
        return user;
      })
      .map(()=>{return this.verifyUserRole})
      .switchMap((response:any)=>{ return response ? Observable.of(true) : Observable.of(false)});
  }

Solution:

感谢@BeetleJuice下面的优秀答案,我能够创建一个衍生解决方案 . 以下是我重构的两种方法:

verifyUserRole(token){
    this.token = token;
    let headers = new Headers({ 'Authorization': 'Bearer ' + token});
    let options = new RequestOptions({ headers: headers });
    return this.http.get(environment.api+'/verifyadminuser', options).map((response)=>{return response});
  }


  verifyAdminUser():Observable<boolean>{
    return this.getToken()
      .switchMap((user)=>{return Observable.fromPromise(user.getIdToken())})
      .switchMap((token)=>{return this.verifyUserRole(token)})
      .map((response:any)=>{ return response.status === 200 ? true : false});
  }

1 回答

  • 5

    请注意这一行,取自 verifyUserRole

    console.log(token);// Log out "PromiseObservable...
    

    所以 token 是一个可观察的,但是你将它视为下一行的一个字符串,所以服务器可能会拒绝该请求

    let headers = new Headers({ 'Authorization': 'Bearer ' + token})
    

    您在 verifyAdminUser() 中滥用 .map 运算符 . map 应仅用于同步功能 . 例如:

    // this works if user.id is a property available immediately
    return this.getToken().map(user => user.id)
    

    map 不应与异步函数一起使用 . 例如:

    // this fails since the return value is an Observable that will resolve later
    return this.getToken().map(user => Observable.fromPromise(...))
    

    map 立即返回 . 因此,传递给链的内容不是您预期的令牌本身,而是将来生成令牌的Observable . 这就是为什么 console.log(token) 给了你"PromiseObservable" . 你需要的是一个等待其中的observable生成的运算符,然后将发出的值传递给下一个运算符 . 使用switchMap

    //this will work; switchMap waits for Obervable.fromPromise 
    // to emit before continuing
    return this.getToken().switchMap(user => Observable.fromPromise(...))
    

    基本上,在 verifyAdminUser 的第3行和第4行中将 map 替换为 switchMap . 您也可以通过反向简化 verifyAdminUser 的最后一行:更改

    .switchMap((response:any)=>{ return response.status === 200 ? Observable.of(true) : Observable.of(false)})
    

    // no need for switchMap because response.status is available immediately
    .map(res => res.status===200? true: false)
    

    此外,您正在使用 canActivate 错误 . 这是为了防止组件的激活 . 要防止加载延迟加载的模块,请使用canLoad guard . 所以我要用 canLoad 替换 canActivate (如果我想保护整个模块),或者将 canActivate 后卫移动到 DashboardRoutingModule 中具有 component: 属性的特定路由(我猜这个名字)

    See the docs

相关问题