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)
注意这条线,取自 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)
所以基本上,替换map用switchMap的线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.所以,我要么替换canActivate用canLoad(如果我要保护整个模块),或移动canActivate后卫具有特定的路线component:内财产DashboardRoutingModule(我猜的名字)