Angular 6-如何在服务中使用Observable来等待直到Okta令牌存在于本地存储中

时间:2018-08-03 02:41:04

标签: angular typescript rxjs

在我的应用程序中,我正在使用第三方身份验证来登录用户,然后在其本地存储中设置令牌。我正在编写一项服务来缓存配置文件信息,该服务使用该用户的身份验证令牌并调用getUser()后端方法来将用户配置文件信息还给我。

问题在于,在本地存储中设置令牌与应用依赖于令牌在初始化时进行后端调用之间的时间之间会有一些延迟。

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)中使用。

我想办法是使getUser()方法返回一个Observable和takeWhile直到值是!== null。或使用timer。尝试了几个小时都没有成功。

非常感谢您的帮助。

__

编辑:

这似乎可行,但是使用timer会让我觉得很hacky,我宁愿换一种方式:

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

3 个答案:

答案 0 :(得分:1)

如我的评论所述,您想要等待this._authService.getUser()完成的问题没有任何意义,因为如果this._authService.getUser()是同步的(如您所述),那么它将始终完成在执行下一行代码之前。

无论如何,在阅读您的代码后,我想我知道您正在尝试做的事情...

  1. 获取用户名表格this._authService.getUser()
  2. 将用户名传递给this._adService.getUser()
  3. 等待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);
  });
}

答案 1 :(得分:0)

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()
)

答案 2 :(得分:0)

我的实现:

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