提问人:kriskanya 提问时间:8/3/2018 最后编辑:kriskanya 更新时间:10/10/2018 访问量:1144
Angular 6 - 如何在服务中使用 Observable 来等待 Okta 令牌存在于 localstorage 中
Angular 6 - How to use an Observable in a service to wait until Okta token exists in localstorage
问:
在我的应用程序中,我使用第三方身份验证来登录用户,然后在他的本地存储中设置令牌。我正在编写一个服务来缓存配置文件信息,该服务采用该用户的身份验证令牌并调用后端方法以返回用户配置文件信息。getUser()
问题在于,在 localstorage 中设置令牌的时间与应用在初始化时依赖令牌进行后端调用的时间之间存在轻微的延迟。
export class UserService {
private userProfileSubject = new BehaviorSubject<Enduser>(new Enduser());
userProfile$ = this.userProfileSubject.asObservable();
constructor(
private _adService: AdService,
private _authService: AuthnService) { }
setUserProfile() {
const username = this._authService.getUser();
this.userProfile$ = this._adService.getUser(username).pipe(
first(),
map(result => result[0]),
publishReplay(1),
refCount()
);
return this.userProfile$;
}
}
这是检查 localstorage 令牌并返回用户名的同步方法。
public getUser(): string {
const jwtHelper = new JwtHelperService()
const token = localStorage.getItem(environment.JWT_TOKEN_NAME);
if (!token || jwtHelper.isTokenExpired(token)) {
return null;
} else {
const t = jwtHelper.decodeToken(token);
return t.username;
}
}
所以需要先完成,然后我才能使用它。this._authService.getUser();
this._adService.getUser(username)
我认为这样做的方法是使该方法返回一个 Observable,直到值为 .或者与 .已经尝试了几个小时,但没有成功。getUser()
takeWhile
!== null
timer
任何帮助将不胜感激。
__
编辑:
这似乎有效,但使用我觉得很骇人听闻,我宁愿以另一种方式做:timer
在:user.service.ts
setUserProfile() {
timer(100).pipe(
concatMap(() => {
const username = this._authService.getUser();
return this._adService.getUser(username)
}),
map(res => res[0])
).subscribe(profile => {
this.userProfileSubject.next(profile);
});
}
在app.component.ts
ngOnInit
this._userService.setUserProfile();
this._userService.userProfile$.pipe(
map((user: Enduser) => this._userService.setUserPermissions(user)),
takeUntil(this.ngUnsubscribe)
).subscribe();
编辑2:工作解决方案
isLoggedIn()
是设置本地存储的方法。在这里,我正在等待它设置完毕,然后再继续获取用户配置文件信息。
this._authService.isLoggedIn().pipe(
concatMap(() => {
const username = this._authService.getUser();
return this._adService.getUser(username)
}),
map(res => res[0])
).subscribe(profile => {
this.userProfileSubject.next(profile);
});
}
isLoggedIn:
isLoggedIn(state): Observable<boolean> {
...
return this.http.get(url, {withCredentials: true}).pipe(
map((res: any) => {
const token = res.mdoc.token;
if (token) {
localStorage.setItem(environment.JWT_TOKEN_NAME, token);
return true;
} else {
return false;
}
})
}
答:
const usernameObs = of(this._authService.getUser());
return usernameObs.pipe(
flatMap(username => {
return this._adService.getUser(username).pipe(
first(),
map(result => result[0]),
publishReplay(1),
refCount()
);
}))
可能有一种方法可以删除嵌套管道。我无法测试它,但这也应该有效,并且更干净一些:
const usernameObs = of(this._authService.getUser());
return usernameObs.pipe(
flatMap(username => {
return this._adService.getUser(username);
}),
first(),
map(result => result[0]),
publishReplay(1),
refCount()
)
评论
this._authService.getUser
takeWhile
正如我的评论中所述,您想要等待完成的问题没有意义,因为如果是同步的(如您所说),那么它总是在执行下一行代码之前完成。this._authService.getUser()
this._authService.getUser()
无论如何,在阅读了您的代码后,我想我知道您要做什么......
- 获取用户名表单
this._authService.getUser()
- 将用户名传递给
this._adService.getUser()
- 等待完成并将其值传递给可观察流,
this._adService.getUser()
userProfile$
为了实现这一点,你不需要任何那些花哨的 RxJS 运算符;您的代码可以像以下简单一样:
export class UserService {
private userProfileSubject = new BehaviorSubject<Enduser>(new Enduser());
userProfile$ = this.userProfileSubject.asObservable();
constructor(
private _adService: AdService,
private _authService: AuthnService
) {}
setUserProfile() {
const username = this._authService.getUser();
this._adService.getUser(username).subscribe((userProfile: Enduser) => {
this.userProfileSubject.next(userProfile);
});
}
}
只需像我上面所做的那样发送到流,并在应用程序中的任何位置订阅它以获取用户配置文件数据。userProfile$
现在,在应用中的任何位置,只要用户配置文件数据向下发送,您都可以执行此操作来获取用户配置文件数据:
constructor(private _userService: UserService) {
_userService.userProfile$.subscribe((userProfile: Enduser) => {
console.log(userProfile);
});
}
评论
this._authService.getUser();
null
console.log(token)
const token = localStorage.getItem(environment.JWT_TOKEN_NAME);
getUser()
token
timeout(100)
setUserProfile()
setUserProfile
setUserProfile
setUserProfile
timer
我的实现:
setUserProfile() {
this.userProfile$ = this._authService.isLoggedIn(this.activatedRoute.snapshot).pipe(
concatMap(() => {
return this._adService.getUser(this._authService.getUser()).pipe(
map(result => result[0]),
publishReplay(1),
refCount()
);
})
)
return this.userProfile$;
}
}
_____
// _adService.getUser()
getUser(username: string): Observable<Enduser> {
const usernameUrl = encodeURIComponent(username);
return this.http.get(`${environment.API_URL}person/${usernameUrl}`).pipe(
map((res: any) => res.data)
);
}
_____
// _authService.getUser()
public getUser(): string {
const jwtHelper = new JwtHelperService()
const token = localStorage.getItem(environment.JWT_TOKEN_NAME);
if (!token || jwtHelper.isTokenExpired(token)) {
return null;
} else {
const t = jwtHelper.decodeToken(token);
return t.username;
}
}
评论
this._authService.getUser()
setUserProfile