【问题标题】:Angular 8 subscribe on finish of multiple succeeding requests, how to?Angular 8在完成多个后续请求时订阅,如何?
【发布时间】:2019-11-21 07:55:19
【问题描述】:

我有一个带有登录方法和登录组件的身份验证服务。

从我的登录组件的 onSubmit 方法中,我调用服务的登录方法并订阅答案。

具有挑战性的部分是,在该登录方法中,我必须执行多个后续请求(一次调用以获取生成的用户名令牌,然后使用该令牌加密输入的密码并再次调用以真正登录在服务器上)。不知道如何做到这一点,尝试了很多方法,但在 Observable 方面我还是很陌生。

登录组件:

    this.authenticationService.login(this.f.username.value, this.f.password.value)
        .pipe(first())
        .subscribe(
            data => {
                this.router.navigate([this.returnUrl]);
            },
            error => {
                this.alertService.error(error);
                this.loading = false;
            });

认证服务:

    login(username, password) {
        return this.http.post<any>(`${config.apiUrl}/users/authenticate`, { username, password })
            .pipe(map(user => {
                // store user details and jwt token in local storage to keep user logged in between page refreshes
                localStorage.setItem('currentUser', JSON.stringify(user));
                this.currentUserSubject.next(user);
                return user;
            }));
    }

我使用 http.get().pipe 和许多变体尝试了多个嵌套调用,但显然我仍然缺少一些基本的理解。

sn-p/idea 来自这里:https://jasonwatmore.com/post/2019/06/10/angular-8-user-registration-and-login-example-tutorial


编辑:

我有一个解决方案,但它很难看,有可能改进吗?

return new Observable((observer) => {
    this.http.get<any>(urlToken)
        .pipe(map(answer => {
            if (answer['type'] != 'Success') { return; }

            let token = answer['message'];
            urlLogin += '&passphrase=' + someSalt(username, password, token);

            return this.http.get<any>(urlLogin)
                .pipe(map(answer2 => {
                    if (answer2['type'] != 'Success') { return; }

                    let sessionId = answer2['message'];
                    let user = new User();
                    user.username = username;
                    user.sessionId = sessionId;

                    localStorage.setItem('currentUser', JSON.stringify(user));
                    this.currentUserSubject.next(user);
                    observer.next(user);
                })).subscribe();
        })).subscribe();
});

【问题讨论】:

    标签: angular angular2-observables angular2-http angular8


    【解决方案1】:

    当以顺序方式链接可观察对象时,使用了称为高阶映射的概念(我建议 this article)。这些高阶映射运算符之一是 concatMap 并将其与 tap 运算符结合使用将帮助您以正确的方式实现目标。您的解决方案不仅丑陋 :( 它不可测试且不必要地复杂。您的 login 方法应该是这样的;

      login(username, password) {
        /** we use concatmap here because return value of this callback is going to be another observable.
         * we are mapping result of an observable to another obervable. higher-order-mapping ;)
         */
        return this.http.get<any>(urlToken).pipe(concatMap(answer => {
          /** notice that we have to return an observable in concatMap callback, that's why we use `of()` */
          if (answer['type'] != 'Success') { return of(false); }
    
          let token = answer['message'];
          urlLogin += '&passphrase=' + someSalt(username, password, token);
    
          /** we use tap here baceuse we are not modifying the response in anyway. we just want to do some
           * side operations without affecting return value of this obervable.
           */
          return this.http.get<any>(urlLogin).pipe(tap(answer2 => {
              /** we just return here beause tap operator is just doing side operations */
              if (answer2['type'] != 'Success') { return; }
    
              let sessionId = answer2['message'];
              let user = new User();
              user.username = username;
              user.sessionId = sessionId;
    
              localStorage.setItem('currentUser', JSON.stringify(user));
              this.currentUserSubject.next(user);
              observer.next(user);      
          }));
        }));
      }
    

    这里是上述概念的演示https://stackblitz.com/edit/angular-qvhay8

    【讨论】:

      【解决方案2】:

      我现在在进行后续调用时经常使用另一种模式。通常干净得多。

      封装方法必须有这样的异步修饰符/前缀

      async doStuff(config: any) {
          const answer1 = await this.service.fetchSth1(config).toPromise();
          const answer2 = await this.service.fetchSth2(answer1).toPromise();
      
          if (answer2) {
              beHappy();
          }
      }
      

      或者在登录的情况下,它现在可能看起来像这样

      async login(name: string, pw: string) {
          const token = await this.authService.getToken(name, pw).toPromise();
          const passphrase = magic(name, pw, token);
          const loginAnswer = await this.authService.login(name, passphrase).toPromise();
          if (loginAnswer[`type`] === 'Success') {
              doStuff();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-01-17
        • 2020-06-16
        • 2018-09-04
        • 1970-01-01
        • 1970-01-01
        • 2019-03-24
        • 2018-10-12
        相关资源
        最近更新 更多