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

cal*_*r47 0 javascript asynchronous rxjs angular

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

基于这个堆栈问题:canActivate Question,我想我正在做的一切正确.但是,唉,我似乎无法让事情发挥作用.

这是模块保护:

@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();
  }
};
Run Code Online (Sandbox Code Playgroud)

这是我的czauth服务:

  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)});
  }
Run Code Online (Sandbox Code Playgroud)

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

编辑:

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 { }
Run Code Online (Sandbox Code Playgroud)

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

编辑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)});
  }
Run Code Online (Sandbox Code Playgroud)

解:

感谢@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});
  }
Run Code Online (Sandbox Code Playgroud)

Bee*_*ice 5

注意这条线,取自 verifyUserRole

console.log(token);// Log out "PromiseObservable...
Run Code Online (Sandbox Code Playgroud)

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

let headers = new Headers({ 'Authorization': 'Bearer ' + token})
Run Code Online (Sandbox Code Playgroud)

你误用了.map操作员verifyAdminUser(). map应该只用于同步功能.例如:

// this works if user.id is a property available immediately
return this.getToken().map(user => user.id)
Run Code Online (Sandbox Code Playgroud)

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

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

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(...))
Run Code Online (Sandbox Code Playgroud)

所以基本上,替换mapswitchMap的线3和4 verifyAdminUser.您也可以verifyAdminUser通过反向简化最后一行:更改

.switchMap((response:any)=>{ return response.status === 200 ? Observable.of(true) : Observable.of(false)})
Run Code Online (Sandbox Code Playgroud)

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

此外,您使用canActivate不正确.这是为了防止组件的激活.要防止加载延迟加载的模块,请使用canLoad guard.所以,我要么替换canActivatecanLoad(如果我要保护整个模块),或移动canActivate后卫具有特定的路线component:内财产DashboardRoutingModule(我猜的名字)

查看文档