如何在带有角度的canActivate中调用异步方法? [英] How to call an async method in canActivate with angular?

查看:34
本文介绍了如何在带有角度的canActivate中调用异步方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to implement a canActivate guard for an admin route in Angular 4.2.4.

基于此处的堆栈问题: canActivate问题,我想我是正确地做一切.但是,a,我似乎无法使事情正常进行.

Based off this stack question here: canActivate Question, I think I'm doing everything correctly. But alas, I can't seem to get things to work.

这是模块防护:

@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();
  }
};

这是我的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)});
  }

我永远无法从我的http异步调用中获得响应.在控制台中,我总能感觉到寒冷.好像路由器从不订阅我的观察者吗?我想根据我的http响应返回一个布尔值.我怎样才能做到这一点?

I can never get the response from my http async call. I always get what looks to be a cold observable in the console. It's as if the router never subscribes to my observable? I would like to return a boolean based off what my http response is. How can I achieve this?

我正在从警卫人员中调用 verifyAdminUser 方法.该服务是根模块上的一个模块.防护措施可保护对延迟加载的模块的访问.

I am calling the verifyAdminUser method from my guard. The service is a sinlgeton on the root module. The guard is protecting access to a lazy-loaded module.

下面,我在路由中包括了我在使用防护装置的位置.

Below I have included where I am using the guard in the routing.

路由模块:

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运算符中的响应未定义.

The trouble is when the user tries to navigate to the dashboard module, canActivate always returns false because the response in my switchMap operator is undefined.

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

I refactored and simplified the following two methods in my code, and now everything works just fine. Now, I'm trying to understand why. Here are the adjusted to methods:

  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)});
  }

解决方案:

感谢下面的@BeetleJuice出色答案,我得以创建一个派生解决方案.这是我重构的两种方法:

Thanks to @BeetleJuice excellent answer below, I was able to create a derivative solution. Here are the two methods I refactored:

  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});
  }

推荐答案

请注意此行,取自 verifyUserRole

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

因此 token 是可以观察到的,但是您将其作为字符串放在下一行,因此服务器可能会拒绝该请求

So token is an observable, but you're treating it as a string on the very next line so the server will probably reject that request

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

您在 verifyAdminUser()中误用了 .map 运算符. map 仅应与同步功能一起使用.例如:

You're misusing the .map operator in verifyAdminUser(). map should only be used with synchronous functions. For example:

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

map 不应与异步功能一起使用.例如:

map should not be used with asynchronous functions. For instance:

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

地图立即返回.结果,沿着链条传递的不是您期望的令牌本身,而是将来会生成令牌的Observable.这就是为什么 console.log(token)给您"PromiseObservable"的原因.您需要一个运算符,它将等待其中的可观察值产生,然后将发出的值传递给下一个运算符.使用 switchMap

map returns immediately. As a result, what gets passed down the chain is not the token itself as you expected, but rather an Observable that will produce the token in the future. That's whyconsole.log(token) gave you "PromiseObservable". What you need is an operator that will wait for the observable in it to produce, then pass the emitted value to the next operator. Use switchMap

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

因此,基本上,在 verifyAdminUser 的第3行和第4行中,用 switchMap 替换 map .您还可以通过执行相反的操作来简化 verifyAdminUser 的最后一行:change

So basically, replace map with switchMap in lines 3 and 4 of verifyAdminUser. You can also simplify the last line of verifyAdminUser by doing the reverse: change

.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保护.因此,我可以将 canActivate 替换为 canLoad (如果我想保护整个模块),或者将 canActivate 防护装置移至特定的路由在 DashboardRoutingModule 中具有 component:属性(我猜是这个名字)

Also, you are using canActivate incorrectly. This is meant to guard against the activation of a component. To guard against the loading of a lazy-loaded module, use canLoad guard. So I would either replace canActivate with canLoad (if I want to protect the entire module), or move the canActivate guard to the specific route that has the component: property within DashboardRoutingModule (I'm guessing at the name)

请参阅文档

这篇关于如何在带有角度的canActivate中调用异步方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆