【问题标题】:Angular2 - Multiple dependent sequential http api callsAngular2 - 多个依赖的顺序 http api 调用
【发布时间】:2016-12-27 11:53:49
【问题描述】:

我正在构建一个 Angular2 应用程序,其中一个组件需要进行多个 API 调用,这些调用依赖于之前的调用。

我目前有一项服务,它通过 API 调用来获取电视节目列表。对于每个节目,我需要多次调用不同的 API 来逐步检查结构以确定该节目是否存在于 Plex 服务器上。

API 文档是here

对于每个节目,我需要进行以下调用并获取正确的数据以确定它是否存在:(假设我们有变量<TVShow>, <Season>, <Episode>

http://baseURL/library/sections/?X-Plex-Token=xyz 会告诉我: title="TV Shows" key="2"

http://baseURL/library/sections/2/all?X-Plex-Token=xyz&title=<TVShow> 会告诉我:key="/library/metadata/2622/children"

http://baseURL/library/metadata/2622/children?X-Plex-Token=xyz 会告诉我:index="<Season>" key="/library/metadata/14365/children"

http://baseURL/library/metadata/14365/children?X-Plex-Token=xyz 会告诉我:index="<Episode>" 这意味着我的情节存在。

回复是 json 格式,我删除了很多多余的文本。在每个阶段,我都需要检查是否存在正确的字段 (<TVShow>, <Season>, <Episode>),以便它们可以用于下一次调用。如果不是,我需要返回该节目不存在。如果是这样,我可能会想为节目返回一个 id。


我查看了很多示例,包括 promise、async 和 flatmap,但根据我看到的其他示例,我不确定如何解决这个问题。


这是我获取节目列表的内容。 (shows.service.ts)

export class ShowsHttpService {
    getShows(): Observable<Show[]> {
        let shows$ = this._http
            .get(this._showHistoryUrl)
            .map(mapShows)
            .catch(this.handleError);
        return shows$;
    }
}

function mapShows(response:Response): Show[] {
    return response.json().data.map(toShow);
}

function toShow(r:any): Show {
    let show = <Show>({
        episode: r.episode,
        show_name: r.show_name,
        season: r.season,
        available : false,    // I need to fill in this variable if the show is available when querying the Plex API mentioned above.
    });
    // My best guess is here would be the right spot to call the Plex API as we are dealing with a single show at a time at this point, but I cannot see how.
    return show;
}

这是来自组件的相关代码(shows.component.ts)

public getShows():any {
    this._ShowsHttpService
        .getShows()
        .subscribe(w => this.shows = w);
    console.log(this.shows);
}

奖励积分

下面是一些有趣但不必要的明显问题:

  1. 第一个 API 查询将比等待所有其他查询发生要快得多(4 个查询 * 约 10 次显示)。是否可以返回初始列表,然后在准备好时使用available 状态进行更新。
  2. 获取key="2" 的第一个 Plex 调用只需执行一次。它可以是硬编码的,但它可以执行一次并记住吗?
  3. 有没有办法减少 API 调用的次数?我可以看到我可以删除显示过滤器,并在客户端上搜索结果,但这也不是很理想。
  4. 每个节目的 4 次调用必须按顺序进行,但每个节目可以并行查询速度。这可以实现吗?

任何想法将不胜感激!

【问题讨论】:

    标签: api http service angular observable


    【解决方案1】:

    不确定我是否完全理解您的问题,但这是我所做的:

    我进行了第一次 http 调用,然后当订阅触发时,它调用了 completeLogin。然后我可以使用它自己的完整函数触发另一个 http 调用并重复该链。

    这是组件代码。用户已填写登录信息并按下登录:

    onSubmit() {
       console.log(' in on submit');
       this.localUser.email = this.loginForm.controls["email"].value;
       this.localUser.password = this.loginForm.controls["password"].value;
       this.loginMessage = "";
       this.checkUserValidation();
    }
    
    checkUserValidation() { 
       this.loginService.getLoggedIn()
          .subscribe(loggedIn => {
             console.log("in logged in user validation")
             if(loggedIn.error != null || loggedIn.error != undefined || loggedIn.error != "") {
                this.loginMessage = loggedIn.error;
             }
          });
    
          this.loginService.validateUser(this.localUser);
    }
    

    这会调用 loginservice ValidateUser 方法

    validateUser(localUser: LocalUser) {
       this.errorMessage = "";
       this.email.email = localUser.email;
       var parm = "validate~~~" + localUser.email + "/"
       var creds = JSON.stringify(this.email);
       var headers = new Headers();
       headers.append("content-type", this.constants.jsonContentType);
    
       console.log("making call to validate");
       this.http.post(this.constants.taskLocalUrl + parm, { headers: headers })
          .map((response: Response) => {
             console.log("json = " + response.json());
             var res = response.json();
             var result = <AdminResponseObject>response.json();
             console.log(" result: " + result);
             return result;
          })
          .subscribe(
             aro => {
                this.aro = aro
             },
             error => {
                console.log("in error");
                var errorObject = JSON.parse(error._body);
                this.errorMessage = errorObject.error_description;
                console.log(this.errorMessage);
             },
             () => this.completeValidateUser(localUser));
                console.log("done with post");
         }
    
    completeValidateUser(localUser: LocalUser) {
       if (this.aro != undefined) {
          if (this.aro.errorMessage != null && this.aro.errorMessage != "") {
             console.log("aro err " + this.aro.errorMessage);
             this.setLoggedIn({ email: localUser.email, password: localUser.password, error: this.aro.errorMessage });
          } else {
             console.log("log in user");
             this.loginUser(localUser);
          }
       } else {
          this.router.navigate(['/verify']);
       }
    

    }

    在我的登录服务中,我调用了返回可观察令牌的授权服务。

    loginUser(localUser: LocalUser) {
       this.auth.loginUser(localUser)
       .subscribe(
          token => {
             console.log('token = ' + token)
             this.token = token
          },
          error => {
             var errorObject = JSON.parse(error._body);
             this.errorMessage = errorObject.error_description;
             console.log(this.errorMessage);
             this.setLoggedIn({ email: "", password: "", error: this.errorMessage });
          },
          () => this.completeLogin(localUser));
    }
    

    在授权服务中:

    loginUser(localUser: LocalUser): Observable<Token> {
       var email = localUser.email;
       var password = localUser.password;
    
        var headers = new Headers();
        headers.append("content-type", this.constants.formEncodedContentType);
    
        var creds:string = this.constants.grantString + email + this.constants.passwordString + password;
        return this.http.post(this.constants.tokenLocalUrl, creds, { headers: headers })
             .map(res => res.json())
    }
    

    这段代码的重点是,首先调用登录服务的validateUser方法,响应后根据返回信息,如果有效,我调用登录服务的loginUser方法。只要您需要,这条链就可以继续。您可以设置类级别的变量来保存链中每个方法所需的信息,以决定下一步该做什么。

    另外请注意,您可以在服务中订阅返回并在那里处理它,它不必返回到组件。

    好的,接下来:

    public getShows():any {
       this._ShowsHttpService
          .getShows()
          .subscribe(
             w => this.shows = w,
             error => this.errorMessage = error,
             () => this.completeGetShows());
    }
    
    completeGetShow() {
    
       //any logic here to deal with previous get;
    
       this.http.get#2()
          .subscribe(
             w => this.??? = w),
             error => this.error = error,
             () => this.completeGet#2);
    }
    
    completeGet#2() {
    
       //any logic here to deal with previous get;
    
       this.http.get#3()
          .subscribe(
             w => this.??? = w),
             error => this.error = error,
             () => this.completeGet#3);
    }
    
    completeGet#3() {
    
       //any logic here to deal with previous get;
    
       //another http: call like above to infinity....
    }
    

    【讨论】:

    • 谢谢约翰。这个概念是正确的,但我看不到如何将它与我现有的代码集成。我目前有一个.get(),然后是.map(),没有.subscribe(),所以不知道在哪里添加这个。此外,我需要为第一个 API 调用返回的每个节目运行我的序列。如果 observable 是错误的方法,我很乐意更改我现有的代码。干杯
    • 约翰,我已经添加了我的组件代码。他们现在看起来很相似。我只能在您的代码中看到一个 http 调用。我同意当第一次调用完成时,应该触发下一次调用,但看不到如何执行此操作并传递正确的数据。我认为应该从服务内部而不是组件中触发下一个调用,因为必须完成更多处理。谢谢
    • 好的...我添加了更多代码来说明我想要表达的观点。
    • John,非常感谢您提供更多代码。不幸的是,我仍然无法将您的代码与我的代码集成。我想我理解你在做什么,但不能让我的匹配。您能否在我的代码中显示我在哪里以及如何开始下一个 HTTP 调用?目前,我仍然没有运气。谢谢
    • 感谢约翰的所有努力。这一切最终都说得通了!我采取了一种稍微不同的方法,并使用 map/flatmap 而不是 () => 将调用串在一起。我稍后会发布它以防其他人想要它,但如果没有你的帮助,我永远不会取得任何进展!
    猜你喜欢
    • 2014-06-02
    • 2021-05-22
    • 2021-03-10
    • 2015-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-19
    • 2018-01-30
    相关资源
    最近更新 更多