Angular 2 - 路由 - CanActivate 与 Observable 一起工作
Posted
技术标签:
【中文标题】Angular 2 - 路由 - CanActivate 与 Observable 一起工作【英文标题】:Angular 2 - Routing - CanActivate work with Observable 【发布时间】:2016-10-23 05:12:35 【问题描述】:我有一个实现 CanActivate 的 AuthGuard(用于路由)。
canActivate()
return this.loginService.isLoggedIn();
我的问题是,CanActivate-result 依赖于 http-get-result - LoginService 返回一个 Observable。
isLoggedIn():Observable<boolean>
return this.http.get(ApiResources.LOGON).map(response => response.ok);
我怎样才能将它们组合在一起 - 使 CanActivate 依赖于后端状态?
######
编辑:请注意,这个问题是从 2016 年开始的 - 角度/路由器的早期阶段已经被使用。
######
【问题讨论】:
你读过这里吗? angular.io/docs/ts/latest/guide/router.html 搜索 Route Guards 这是 CanActivate 的 api 参考:angular.io/docs/ts/latest/api/router/index/… 如您所见,它可以返回布尔值或 ObservablecanActivate()
可以返回一个Observable
,只要确保Observable
已经完成(即observer.complete()
)。
@PhilipBulley 如果 observable 发出更多值然后完成怎么办?守门员是做什么的?目前我看到的是使用take(1)
Rx操作符来实现stream的completnes,如果忘记添加怎么办?
【参考方案1】:
在 canActivate() 中,您可以返回一个本地布尔属性(在您的情况下默认为 false)。
private _canActivate: boolean = false;
canActivate()
return this._canActivate;
然后在LoginService的结果中,你可以修改那个属性的值。
//...
this.loginService.login().subscribe(success => this._canActivate = true);
【讨论】:
你是个天才,先生。【参考方案2】:您应该将“@angular/router”升级到最新的 .例如“3.0.0-alpha.8”
修改 AuthGuard.ts
@Injectable()
export class AuthGuard implements CanActivate
constructor(private loginService: LoginService, private router: Router)
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot)
return this.loginService
.isLoggedIn()
.map((e) =>
if (e)
return true;
)
.catch(() =>
this.router.navigate(['/login']);
return Observable.of(false);
);
如果你有任何问题,可以问我!
【讨论】:
值得指出的是,这与 Promise 的工作方式非常相似。对于我的实现,假设isLoggedIn()
是Promise
,您可以使用isLoggedIn().then((e) => if (e) return true; ).catch(() => return false; );
希望这对未来的旅行者有所帮助!
我必须添加import 'rxjs/add/observable/of';
现在不是一个好的答案 IMO .. 它没有提供服务器端正在发生的事情的详细信息......它在 alpha 时已经过时了! ...它不遵循此最佳实践..angular.io/docs/ts/latest/guide/… ..请参阅下面的更新答案(希望一天以上)
canActivate 应该重新调整 Observableimport Observable, of from 'rxjs';
【参考方案3】:
我是这样做的:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>
return this.userService.auth(() => this.router.navigate(['/user/sign-in']));
如您所见,我正在向 userService.auth 发送一个回退函数,如果 http 调用失败该怎么办。
在 userService 我有:
import 'rxjs/add/observable/of';
auth(fallback): Observable<boolean>
return this.http.get(environment.API_URL + '/user/profile', withCredentials: true )
.map(() => true).catch(() =>
fallback();
return Observable.of(false);
);
【讨论】:
就 .map() 函数而言非常好的答案 - 非常非常好:) - 没有使用回退回调 - 相反,我订阅了 canActivate 方法中的 auth Observable - 非常感谢想法【参考方案4】:canActivate() 接受 Observable<boolean>
作为返回值。守卫将等待 Observable 解析并查看值。如果为“真”,它将通过检查,否则(任何其他数据或抛出的错误)将拒绝路由。
您可以使用.map
运算符将Observable<Response>
转换为Observable<boolean>
,如下所示:
canActivate()
return this.http.login().map((res: Response)=>
if ( res.status === 200 ) return true;
return false;
);
【讨论】:
catch 块怎么样?如果 401 正确,则调用 catch 块? 在角度 4 中不起作用。它需要在某个地方定义一个泛型类型。【参考方案5】:CanActivate 确实可以与 Observable 一起使用,但在进行 2 次调用时失败,例如 CanActivate:[Guard1, Guard2]。 在这里,如果您从 Guard1 返回一个 false 的 Observable,那么它也会检查 Guard2 并在 Guard2 返回 true 时允许访问路由。 为了避免这种情况,Guard1 应该返回一个布尔值而不是布尔值的 Observable。
【讨论】:
【参考方案6】:更新 Kery Hu 对 Angular 5+ 和 RxJS 5.5 where the catch
operator is deprecated. You should now use the catchError operator in conjunction with pipe 和 lettable operators 的回答。
import Injectable from '@angular/core';
import CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot from '@angular/router';
import Observable from 'rxjs/Observable';
import catchError, map from 'rxjs/operators';
import of from 'rxjs/observable/of';
@Injectable()
export class AuthGuard implements CanActivate
constructor(private loginService: LoginService, private router: Router)
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>
return this.loginService.isLoggedIn().pipe(
map(e =>
if (e)
return true;
else
...
),
catchError((err) =>
this.router.navigate(['/login']);
return of(false);
)
);
【讨论】:
在 isLoggedIn() 之后我还有 1 个 XHR 调用,并且 XHR 的结果用于第二个 XHR 调用。如何进行第二个将接受第一个结果的 ajax 调用?你给出的例子很简单,如果我也有另一个 ajax,你能告诉我如何使用 pipe()。 对于 angular 10 也非常有用 谢谢。你救了我的一天!对 Angular 11 有用。【参考方案7】:这可能对你有帮助
import Injectable from '@angular/core';
import CanActivate, Router from '@angular/router';
import Select from '@ngxs/store';
import Observable from 'rxjs';
import map, take from 'rxjs/operators';
import AuthState from 'src/app/shared/state';
export const ROLE_SUPER = 'ROLE_SUPER';
@Injectable()
export class AdminGuard implements CanActivate
@Select(AuthState.userRole)
private userRoles$: Observable<string[]>;
constructor(private router: Router)
/**
* @description Checks the user role and navigate based on it
*/
canActivate(): Observable<boolean>
return this.userRoles$.pipe(
take(1),
map(userRole =>
console.log(userRole);
if (!userRole)
return false;
if (userRole.indexOf(ROLE_SUPER) > -1)
return true;
else
this.router.navigate(['/login']);
return false;
)
);
// canActivate()
// class
【讨论】:
以上是关于Angular 2 - 路由 - CanActivate 与 Observable 一起工作的主要内容,如果未能解决你的问题,请参考以下文章
Angular 2 - 如何使用新的 Angular 2.0.0-rc.1 路由器