Angular2 - 多个依赖的顺序http api调用

问题描述:

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

I am building an Angular2 app and one of the components needs to make multiple API calls which are dependent on the previous ones.

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

I currently have a service which makes an API call to get a list of TV shows. For each show, I then need to call a different API multiple times to step through the structure to determine if the show exists on a Plex server.

API文档是这里

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

For each show, I need to make the following calls and get the correct data to determine if it exists: (Assume we have variables <TVShow>, <Season>, <Episode>)

http:// baseURL / library / sections /?X-Plex-Token = xyz 会告诉我:
title =电视节目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 /儿童?X-Plex-Token = xyz 会告诉我: index =< Episode>这意味着我的剧集存在。

http://baseURL/library/metadata/14365/children?X-Plex-Token=xyz will tell me: index="<Episode>" which implies that the episode I have exists.

响应在json中,我删除了很多多余的文本。在每个阶段,我需要检查是否存在正确的字段(< TVShow>,< Season>,< Episode> ),以便它们可以用于下一个呼叫。如果没有,我需要返回该节目不存在。如果是的话,我可能会想要为节目返回一个id。

The responses are in json, I have removed a lot of the excess text. At each stage I need to check that the right fields exist (<TVShow>, <Season>, <Episode>) so that they can be used for the next call. If not, I need to return that the show does not exist. If it does, I will probably want to return an id for the show.

我看了很多例子,包括承诺,异步和flatmap,但我不知道如何根据我见过的其他例子来解决这个问题。

I have looked at lots of examples including promise, async & flatmap, but am not sure how to solve this based on the other examples I have seen.

  • How to chain Http calls in Angular2
  • Angular 2.0 And Http
  • Angular 2 - What to do when an Http request depends on result of another Http request
  • Angular 2 chained Http Get Requests with Iterable Array
  • nodejs async: multiple dependant HTTP API calls
  • How to gather the result of Web APIs on nodeJS with 'request' and 'async'

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

Here is what I have for getting the list of shows. (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)

Here is the relevant code from the component (shows.component.ts)

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






奖励积分



以下是明显的下一个有趣的问题,但不是必需的:


Bonus points

Here are the obvious next questions that are interesting, but not necessary:


  1. 第一个API查询将比等待所有其他查询快得多(4个查询* ~10个节目)。可以返回初始列表,然后在准备就绪时使用可用状态进行更新。

  2. 第一个Plex调用来获取 key =2只需要执行一次。它可能是硬编码的,但相反,它可以执行一次并记住吗?

  3. 有没有办法减少API调用的数量?我可以看到我可以删除节目过滤器,并在客户端上搜索结果,但这也不理想。

  4. 每个节目的4个调用必须按顺序完成,但每个节目可以并行查询速度。这是可以实现的吗?

  1. The first API query will be much faster than waiting for all of the other queries to take place (4 queries * ~10 shows). Can the initial list be returned and then updated with the available status when it is ready.
  2. The first Plex call to get the key="2" only needs to be performed once. It could be hard coded, but instead, can it be performmed once and remembered?
  3. Is there a way to reduce the number of API calls? I can see that I could remove the show filter, and search through the results on the client, but this doesn't seam ideal either.
  4. The 4 calls for each show must be done sequentially, but each show can be queried in parallel for speed. Is this achievable?

任何想法都会非常感激!

Any thoughts would be much appreciated!

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

Not sure if I totally understand your question, but here is what I do:

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

I make the first http call, then when the subscribe fires, it calls completeLogin. I could then fire another http call with its own complete function and repeat the chain.

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

Here is the component code. The user has filled in the login information and pressed login:

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方法

This calls the loginservice ValidateUser method

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

}

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

In my login service I make a call to the authorization service which returns an observable of token.

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方法。只要你需要,这条链就可以继续。您可以设置类级变量来保存链中每个方法所需的信息,以便决定下一步该做什么。

The point here in this code, is to first call the validateUser method of the login service, upon response, based on the return information, if its valid, I call the loginUser method on the login service. This chain could continue as long as you need it to. You can set class level variables to hold the information that you need in each method of the chain to make decisions on what to do next.

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

Notice also that you can subscribe to the return in the service and process it there, it doesn't have to return to the component.

好的,接下来:

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