Return observable inside observable in canactivate guard of Angular 4
Return observable inside observable in canactivate guard of Angular 4
所以我必须在路由器的 canActivate 守卫中做两件事。首先,我必须检查用户是否获得授权,我通过调用 OpenIdcSecutiry 服务来执行此操作,然后如果用户获得授权,我必须通过调用 returns UserProfile(包括其角色)的另一个服务来检查用户角色。我知道我可以 return 一个 Observable,事实上如果我这样做:
return this.oidcSecurityService.getIsAuthorized();
有效,但显然我没有检查用户角色。现在我有这个:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.oidcSecurityService.getIsAuthorized()
.map(
data => {
if (data === false) {
this.router.navigate(['/login']);
return data;
}
if (data === true) {
const roles = route.data['roles'] as Array<string>;
if (roles) {
this.profileService.getObservableUserProfileFromServer().map(userProfile => {
const userRoles = userProfile.Roles;
for (const role of roles) {
if (userRoles.indexOf(role) > -1) {
return true;
}
}
return false;
});
}
return data;
}
},
error => {
this.router.navigate(['/login']);
return error;
}
);
this.profileService.getObservableUserProfileFromServer() 永远不会被调用,我不知道如何在知道用户获得授权后检查用户角色。我怎样才能做到这一点?我可以 return 我的 observable 里面的 observable 吗?
EDIT1:我将代码更改为:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.oidcSecurityService.getIsAuthorized()
.map(
data => {
if (data === false) {
this.router.navigate(['/login']);
return data;
}
if (data === true) {
const roles = route.data['roles'] as Array<string>;
if (roles) {
return this.profileService.getObservableUserProfileFromServer().toPromise().then(userProfile => {
const userRoles = userProfile.Roles;
for (const role of roles) {
if (userRoles.indexOf(role) > -1) {
return true;
}
}
return false;
}
);
}
return data;
}
},
error => {
this.router.navigate(['/login']);
return error;
}
);
// return this.oidcSecurityService.getIsAuthorized();
}
现在我可以看到如何调用 this.profileService.getObservableUserProfileFromServer 并从服务器接收结果,但是结果没有在 canActivate 守卫中评估,它只是忽略了结果,它说导航被取消了。
编辑决赛:
@llai 给了我答案,必须用两个 switchmap 链接几个 observable,最终代码在这里:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.oidcSecurityService.getIsAuthorized()
.switchMap(
data => {
if (data === false) {
this.router.navigate(['/login']);
return Observable.of(false); // user not logged in, canActivate = false
} else if (data === true) {
const roles = route.data['roles'] as Array<string>;
if (roles) {
// return inner observable
return this.profileService.getObservableUserProfileFromServer()
.switchMap(userProfile => {
const userRoles = userProfile.Roles;
for (const role of roles) {
if (userRoles.indexOf(role) > -1) {
// role found, canActivate = true
return Observable.of(true);
}
}
// no matching role, canActivate = false
return Observable.of(false);
});
} else {
// no roles defined in route data, canActivate = false
return Observable.of(true);
}
}
});
}
正如 Christian 在评论中提到的那样,您应该使用 switchMap
将您的可观察对象链接在一起。
在您的第一个实现中,您没有 returning 内部可观察对象,因此路由器从不订阅它,因此它永远不会被调用。
在您的第二个实现中,您是 "subscribing"(转换为 promise + then)到 map 函数中的可观察对象,这使得内部可观察对象着火。然而,由于 observable 没有 returned 给守卫,守卫不会等待内部 observable 完成。
相反,您应该将 observables 和 return 链接到守卫的序列。这将允许守卫订阅序列并等待正确的响应。
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.oidcSecurityService.getIsAuthorized()
.switchMap(
data => {
if (data === false) {
this.router.navigate(['/login']);
return Observable.of(false); // user not logged in, canActivate = false
}else if (data === true) {
const roles = route.data['roles'] as Array<string>;
if (roles) {
// return inner observable
return this.profileService.getObservableUserProfileFromServer()
.map(userProfile => {
const userRoles = userProfile.Roles;
for (const role of roles) {
if (userRoles.indexOf(role) > -1) {
// role found, canActivate = true
return true;
}
}
// no matching role, canActivate = false
return false;
});
}else{
// no roles defined in route data, canActivate = false
return Observable.of(false)
}
}
}
}
);
}
所以我必须在路由器的 canActivate 守卫中做两件事。首先,我必须检查用户是否获得授权,我通过调用 OpenIdcSecutiry 服务来执行此操作,然后如果用户获得授权,我必须通过调用 returns UserProfile(包括其角色)的另一个服务来检查用户角色。我知道我可以 return 一个 Observable,事实上如果我这样做:
return this.oidcSecurityService.getIsAuthorized();
有效,但显然我没有检查用户角色。现在我有这个:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.oidcSecurityService.getIsAuthorized()
.map(
data => {
if (data === false) {
this.router.navigate(['/login']);
return data;
}
if (data === true) {
const roles = route.data['roles'] as Array<string>;
if (roles) {
this.profileService.getObservableUserProfileFromServer().map(userProfile => {
const userRoles = userProfile.Roles;
for (const role of roles) {
if (userRoles.indexOf(role) > -1) {
return true;
}
}
return false;
});
}
return data;
}
},
error => {
this.router.navigate(['/login']);
return error;
}
);
this.profileService.getObservableUserProfileFromServer() 永远不会被调用,我不知道如何在知道用户获得授权后检查用户角色。我怎样才能做到这一点?我可以 return 我的 observable 里面的 observable 吗?
EDIT1:我将代码更改为:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.oidcSecurityService.getIsAuthorized()
.map(
data => {
if (data === false) {
this.router.navigate(['/login']);
return data;
}
if (data === true) {
const roles = route.data['roles'] as Array<string>;
if (roles) {
return this.profileService.getObservableUserProfileFromServer().toPromise().then(userProfile => {
const userRoles = userProfile.Roles;
for (const role of roles) {
if (userRoles.indexOf(role) > -1) {
return true;
}
}
return false;
}
);
}
return data;
}
},
error => {
this.router.navigate(['/login']);
return error;
}
);
// return this.oidcSecurityService.getIsAuthorized();
}
现在我可以看到如何调用 this.profileService.getObservableUserProfileFromServer 并从服务器接收结果,但是结果没有在 canActivate 守卫中评估,它只是忽略了结果,它说导航被取消了。
编辑决赛:
@llai 给了我答案,必须用两个 switchmap 链接几个 observable,最终代码在这里:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.oidcSecurityService.getIsAuthorized()
.switchMap(
data => {
if (data === false) {
this.router.navigate(['/login']);
return Observable.of(false); // user not logged in, canActivate = false
} else if (data === true) {
const roles = route.data['roles'] as Array<string>;
if (roles) {
// return inner observable
return this.profileService.getObservableUserProfileFromServer()
.switchMap(userProfile => {
const userRoles = userProfile.Roles;
for (const role of roles) {
if (userRoles.indexOf(role) > -1) {
// role found, canActivate = true
return Observable.of(true);
}
}
// no matching role, canActivate = false
return Observable.of(false);
});
} else {
// no roles defined in route data, canActivate = false
return Observable.of(true);
}
}
});
}
正如 Christian 在评论中提到的那样,您应该使用 switchMap
将您的可观察对象链接在一起。
在您的第一个实现中,您没有 returning 内部可观察对象,因此路由器从不订阅它,因此它永远不会被调用。
在您的第二个实现中,您是 "subscribing"(转换为 promise + then)到 map 函数中的可观察对象,这使得内部可观察对象着火。然而,由于 observable 没有 returned 给守卫,守卫不会等待内部 observable 完成。
相反,您应该将 observables 和 return 链接到守卫的序列。这将允许守卫订阅序列并等待正确的响应。
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.oidcSecurityService.getIsAuthorized()
.switchMap(
data => {
if (data === false) {
this.router.navigate(['/login']);
return Observable.of(false); // user not logged in, canActivate = false
}else if (data === true) {
const roles = route.data['roles'] as Array<string>;
if (roles) {
// return inner observable
return this.profileService.getObservableUserProfileFromServer()
.map(userProfile => {
const userRoles = userProfile.Roles;
for (const role of roles) {
if (userRoles.indexOf(role) > -1) {
// role found, canActivate = true
return true;
}
}
// no matching role, canActivate = false
return false;
});
}else{
// no roles defined in route data, canActivate = false
return Observable.of(false)
}
}
}
}
);
}