Express - 来自不同端点的多个异步请求

时间:2021-01-01 16:49:39

I'm developing an app with NodeJS and Express. The objective of this app is to provide data that is fetched from multiple different API endpoints, all in one single page.

我正在使用NodeJS和Express开发应用程序。此应用程序的目标是提供从多个不同API端点获取的数据,所有这些都在一个页面中。

Unfortunately, I'm relatively new with NodeJS and Express but already have done some research and I know that you can make multiple asyncronous requests with the async module. However, I'm not quite sure how to approach this problem and need your help to get me back on the track.

不幸的是,我对NodeJS和Express相对较新,但已经做过一些研究,我知道你可以用异步模块做出多个异步请求。但是,我不太确定如何处理这个问题,需要你的帮助才能让我回到赛道上。

I need to provide two asynchronous requests to get all the expected data and eventually link them together as one JSON. The requets are as follows:

我需要提供两个异步请求来获取所有预期的数据,并最终将它们作为一个JSON链接在一起。 requets如下:

Request 1 - Tonight's Games

请求1 - 今晚的比赛

First, I need to get the scheduled games for tonight (http://exampleapi.com/api/v1/schedule) Outputted JSON data from this API endpoint is as follows:

首先,我需要获得今晚的预定游戏(http://exampleapi.com/api/v1/schedule)此API端点输出的JSON数据如下:

{
  "date": "2016-12-15",
  "totalGames": 3,
  "games": [
     {
       "id": 1,
       "link": "/api/v1/game/1"
     },
     {
       "id": 2,
       "link": "/api/v1/game/2"
     },
     {
       "id": 3,
       "link": "/api/v1/game/3"
     }
  ]
}

Underneath, there is a route from my file that does output the fetched JSON in a way explained above.

在下面,有一个来自我的文件的路径,它以上面解释的方式输出获取的JSON。

Side note/problem: While the for loop iterates over 3 times, a first object will be outputted three times instead of three different objects.

旁注/问题:当for循环迭代3次以上时,第一个对象将输出三次而不是三个不同的对象。

server.js

server.js

router.get('/test/schedule_test', function(req, res) {

    res.contentType('application/json'); // content type of the response object
    var url = 'http://exampleapi.com/api/v1/schedule'; // url of the api endpoint

    // make an request to the endpoint
    request(url, function (error, response, body) {

        // If the request was successfully made
        if (!error && response.statusCode == 200) {

            var body = JSON.parse(body);
            var schedule, totalGames, date, games = [], id, link, game;

            // Schedule object
            schedule = {
                "totalGames": totalGames,
                "date": date,
                "games": games
            };
            schedule.totalGames = body.totalGames;
            schedule.date = body.dates[0].date;
            game = {"id": id, "link": link};

            for(var i = 0; i < schedule.totalGames; i++) {
                // Here's the part where I think I've made some mistakes and why it outputs a single object over so many times the value of "totalGames" is.
                game.id = body.dates[0].games[i].gamePk;
                game.link = body.dates[0].games[i].link;

                games.push({
                    "game": game
                });
            }
            res.status(200).json(schedule);

        } else {
            res.status(404).json({"error": true});
        }
    });
});

Next, I need to make an asynchronous request for each of the links provided by the link property value to fetch the the team-related data of each participating teams.

接下来,我需要为链接属性值提供的每个链接发出异步请求,以获取每个参与团队的团队相关数据。

Request 2 - Team-related data

请求2 - 与团队相关的数据

Expected JSON output from this API endpoint is as follows:

此API端点的预期JSON输出如下:

{
  "gameData": {
    "teams": {
      "away": {
        "link": "/api/v1/teams/1",
        "name": "Example team 1",
        "abbreviation": "EXT1"
       },

       "home": {
         "link": "/api/v1/teams/2",
         "name": "Example team 2",
         "abbreviation": "EXT2"    
        }
    },
    "venue": {
      "name": "Georgestown Palace, Manitoba"
    }
  }
}

Underneath, there is a route from my file that does output the fetched JSON in a way explained above.

在下面,有一个来自我的文件的路径,它以上面解释的方式输出获取的JSON。

server.js

server.js

router.get('/test/team_test', function(req, res) {

    // Note that these urls should be generated dynamically from the previous request
    var urls = [
        'http://exampleapi.com/api/v1/teams/1',
        'http://exampleapi.com/api/v1/teams/2'
    ];

    async.map(urls, function(url, callback) {
        // iterator function
        request(url, function (error, response, body) {

            if (!error && response.statusCode == 200) {
                // do any further processing of the data here
                var body = JSON.parse(body);
                var teams;

                // Away team
                var away_link, away_name, away_abbr;
                var away = {"link": away_link, "name": away_name, "abbreviation": away_abbr};

                var away_link = body.gameData.teams.away.link;
                away.link = away_link;

                var away_name = body.gameData.teams.away.name;
                away.name = away_name;

                var abbreviation = body.gameData.teams.away.abbreviation;
                away.abbrreviation = abbreviation;

                // Home team
                var home_link, home_name, home_abbr;
                var home = {"link": home_link, "name": home_name, "abbreviation": home_abbr};

                var home_link = body.gameData.teams.home.link;
                home.link = home_link;

                var home_name = body.gameData.teams.home.name;
                home.name = home_name;

                var home_abbreviation = body.gameData.teams.home.abbreviation;
                home.abbreviation = home_abbreviation;

                var arena = body.gameData.venue.name;

                teams = {
                    "teams": {
                        "away": away,
                        "home": home
                    },
                    "played_at": arena

                };

                callback(null, teams);

            } else {
                callback(error || response.statusCode);
            }
        });
    }, function(err, results) {

        // completion function
        if (!err) {
            res.contentType('application/json');
            res.status(200).json(results);
        } else {
            // handle error here
            console.log(err);
        }
    });
});

Expected Result

预期结果

Lastly, I need to join these two different outputs into one JSON output. The expected result would look something like this:

最后,我需要将这两个不同的输出连接到一个JSON输出中。预期的结果看起来像这样:

{
  "date": "2016-12-15",
  "totalGames": 3,
  "games": [
     {
       // #1 joined object

       // data from the first api request
       "id": 1,
       "link": "/api/v1/game/1",

       // data from the second api request
       "teams": {
          "away": {
            "link": "/api/v1/teams/1",
            "name": "Example team 1",
            "abbreviation": "EXT1"
          },
          "home": {
            "link": "/api/v1/teams/2",
            "name": "Example team 2",
            "abbreviation": "EXT2"    
           }
        },
        "venue": {
          "name": "Georgestown Palace, Manitoba"
        }
     },


     {
       // #2 joined object
       "id": 2,
       "link": "/api/v1/game/3",
       "teams": {
          "away": {
            "link": "/api/v1/teams/3",
            "name": "Example team 3",
            "abbreviation": "EXT3"
          },
          "home": {
            "link": "/api/v1/teams/4",
            "name": "Example team 4",
            "abbreviation": "EXT4"    
           }
        },
        "venue": {
          "name": "Portsmouth Valley, New Jersey"
        }
     },

     {
       // #3 joined object
       "id": 3,
       "link": "/api/v1/game/3",
       "teams": {
          "away": {
            "link": "/api/v1/teams/5",
            "name": "Example team 5",
            "abbreviation": "EXT5"
          },
          "home": {
            "link": "/api/v1/teams/6",
            "name": "Example team 6",
            "abbreviation": "EXT6"    
           }
        },
        "venue": {
          "name": "Colorado Springs, Colorado"
        }

     }

  ]

}

Any help would be much appreciated. Thanks in advance and happy coding!

任何帮助将非常感激。在此先感谢您的编码!

1 个解决方案

#1


0  

For coordinating multiple asynchronous operations, I'd strongly recommend that you use promises. If you make each one of your async operations return a promise, then you can use Promise.all() to know when they are all done and to have access to the data that each async operation fetched.

为了协调多个异步操作,我强烈建议您使用promises。如果您使每个异步操作都返回一个promise,那么您可以使用Promise.all()来知道它们何时完成,并且可以访问每个异步操作获取的数据。

For example, if you had an array of endpoint that you wanted to fetch data from, you could do that like this:

例如,如果您有一个想要从中获取数据的端点数组,则可以这样做:

const request = require('request');

// promisify the request module
function requestAsync(url) {
    return new Promise(function(resolve, reject) {
        request(url, function(err, response, body) {
            if (err) {
                reject(err);
            } else if (response.statusCode !== 200) {
                reject(new Error("response.statusCode = " + response.statusCode));
            } else {
                resolve(body);
            }
        });
    });
}


function getData(endpoints) {
    return Promise.all(endpoints.map(function(url) {
        return requestAsync(url);
    }));
}

let endpoints = [...];    // multiple endpoints to retrieve data from
getData(endpoints).then(function(results) {
    // results array contains all the results you can process here in the
    // order they appears in the array.
    // You can now organize the returned data into the final format you want here
}).catch(function(err) {
    // error occurred here
});

#1


0  

For coordinating multiple asynchronous operations, I'd strongly recommend that you use promises. If you make each one of your async operations return a promise, then you can use Promise.all() to know when they are all done and to have access to the data that each async operation fetched.

为了协调多个异步操作,我强烈建议您使用promises。如果您使每个异步操作都返回一个promise,那么您可以使用Promise.all()来知道它们何时完成,并且可以访问每个异步操作获取的数据。

For example, if you had an array of endpoint that you wanted to fetch data from, you could do that like this:

例如,如果您有一个想要从中获取数据的端点数组,则可以这样做:

const request = require('request');

// promisify the request module
function requestAsync(url) {
    return new Promise(function(resolve, reject) {
        request(url, function(err, response, body) {
            if (err) {
                reject(err);
            } else if (response.statusCode !== 200) {
                reject(new Error("response.statusCode = " + response.statusCode));
            } else {
                resolve(body);
            }
        });
    });
}


function getData(endpoints) {
    return Promise.all(endpoints.map(function(url) {
        return requestAsync(url);
    }));
}

let endpoints = [...];    // multiple endpoints to retrieve data from
getData(endpoints).then(function(results) {
    // results array contains all the results you can process here in the
    // order they appears in the array.
    // You can now organize the returned data into the final format you want here
}).catch(function(err) {
    // error occurred here
});