【问题标题】:Returning Observables from chained HTTP call using concatMap使用 concatMap 从链式 HTTP 调用返回 Observables
【发布时间】:2020-05-21 09:54:44
【问题描述】:

在 Angular 9 服务类中,我使用 RxJS 的 concatMap 链接了几个 HTTP 调用,使用第一个调用的输出作为第二个调用的输入:

getUserDetails(authorisationCode: string): Observable<UserInfo> {

this.getAuthorisationTokens(authorisationCode)
  .pipe(
    concatMap(authorisationTokens =>  this.getUserInfo(authorisationTokens.access_token)))
    .subscribe(
      (data: UserInfo) => {
        this.userInfo = data;
        console.log(JSON.stringify(this.userInfo));
        console.log(JSON.stringify(data));
      },
      (err: any) => console.log('Error getting user info details : ' + err),
      () => {
        console.log('Got user information: ' + this.userInfo);
      }
    );

return of(this.userInfo);
}

我想将this.userInfo 返回给调用者,我的第一个天真的想法是将它包装在一个 Observable (return of(this.userInfo)) 中并像这样调用它:

export class LandingComponent implements OnInit {

  username: string;
  userFirstName: string;
  email: string;


  constructor(private route: ActivatedRoute, private userService: UserDataService) {
    this.authorisationCode = route.snapshot.queryParamMap.get('code');
    console.log('Code was ' + this.authorisationCode);
  }

  ngOnInit(): void {

    this.userService.getUserDetails(this.authorisationCode)
      .subscribe((data: UserInfo) => {
        this.userFirstName = data.given_name;
        this.username = data.preferred_username;
        this.email = data.email;

        console.log('Got: ' + this.userFirstName + ', ' + this.username + ', ' + this.email);
      });

  }
}

我可以从浏览器控制台看到对我的服务的调用正在成功并填充我的对象this.userInfo,但只有在我尝试使用它并收到未定义的错误之后:

Code was 2ffa40f9-5e71-4f29-8ddd-318e8d0b99bc
main-es2015.8df8d853b157ca70b40a.js:1 Getting authorisation tokens in exchange for authorisation code 2ffa40f9-5e71-4f29-8ddd-318e8d0b99bc
main-es2015.8df8d853b157ca70b40a.js:1 Header: [object Object]
main-es2015.8df8d853b157ca70b40a.js:1 Body: grant_type=authorization_code&redirect_uri=https://xxx/landing/&client_id=xxx&code=2ffa40f9-5e71-4f29-8ddd-318e8d0b99bc&client_secret=xxx
main-es2015.8df8d853b157ca70b40a.js:1 TOKEN endpoint: https://xxx.amazoncognito.com/oauth2/token

TOKEN endpoint: https://xxx.amazoncognito.com/oauth2/token
main-es2015.8df8d853b157ca70b40a.js:1 ERROR TypeError: Cannot read property 'given_name' of undefined

    ...

USERINFO endpoint https://xxx.amazoncognito.com/oauth2/userInfo
main-es2015.8df8d853b157ca70b40a.js:1 USERINFO endpoint https://xxx.amazoncognito.com/oauth2/userInfo
main-es2015.8df8d853b157ca70b40a.js:1 {"sub":"4bfd88a4-5439-4ad6-a399-71b02034dfa1","email_verified":"true","given_name":"Craig","family_name":"Caulfield","email":"craig.caulfield@xxx.com","username":"4bfd88a4-5439-4ad6-a399-xxx"}
main-es2015.8df8d853b157ca70b40a.js:1 Got user information: [object Object]

我已尝试应用以下问题,但找不到将它们应用到我的案例的方法:

所以,我的异步想法是错误的。有什么我没有做过的明显的事情吗?

【问题讨论】:

    标签: angular typescript rxjs


    【解决方案1】:

    您可能会返回 undefined,因为 this.userInfo 是异步赋值的。一种正确的方法是返回 HTTP observable 并在控制器中订阅它。

    我还将authorisationCodeconstructor 提取到ngOnInit() 挂钩。虽然通常ngOnInit() 挂钩会在constructor 被调用后触发,但不能保证在触发挂钩时变量this.authorisationCode 已经被赋值。

    试试下面的

    服务

    getUserDetails(authorisationCode: string): Observable<any> {
      return this.getAuthorisationTokens(authorisationCode)
        .pipe(
          concatMap(authorisationTokens =>  this.getUserInfo(authorisationTokens.access_token))
        );
    }
    

    控制器

    constructor(private route: ActivatedRoute, private userService: UserDataService) { }
    
    ngOnInit(): void {
      const authorisationCode = route.snapshot.queryParamMap.get('code');
      this.userService.getUserDetails(authorisationCode).subscribe(
        (data: UserInfo) => {
          this.userFirstName = data.given_name;
          this.username = data.preferred_username;
          this.email = data.email;
    
          console.log('Got: ' + this.userFirstName + ', ' + this.username + ', ' + this.email);
        },
        (err: any) => { console.log('Error getting user info details : ' + err) },
        () => { console.log('Got user information: ' + data); }
      );
    }
    

    【讨论】:

    • 效果很好。因此,模式似乎是:在您将使用数据的位置附近执行subscribe
    • 任何直接依赖异步数据的数据/函数都应该在订阅中分配/调用。你可以参考这里了解更多关于异步数据的信息:stackoverflow.com/a/14220323/6513921
    【解决方案2】:

    当你第一次通过 observable 访问 this.UserInfo 时,它是未定义的。您需要等到 API 返回,否则您必须忽略由 of(this.userUnfo) 创建的 observable 发出的第一个值(例如:应用 skip(1) 运算符)。 更好的解决方案是仅在 API 返回后触发 this.user 可观察对象。您可以创建一个主题,它会在 API 完成后触发,而不是返回 of(this.userInfo) yiu 只需从上述主题中创建可观察对象(例如:用户 ToObservable() 操作)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-24
      • 2018-12-24
      • 2020-10-06
      • 2017-04-12
      • 2016-05-18
      • 1970-01-01
      相关资源
      最近更新 更多