Node JS,从两个链接同步发出HTTPS请求

时间:2016-02-10 21:49:24

标签: javascript json node.js api rest

我想通过Node JS向外部链接发出HTTPS请求。在我的第一次通话中,我需要通过循环遍历多个用户来获取用户ID。在我的第二次调用中,我需要在URL链接中输入该用户ID并获取用户属性。继续重复这个过程,直到我通过所有用户。最终目标是以JSON格式存储每个用户的数据。没有涉及前端。非常感谢任何方向/建议。

由于api密钥,我无法共享实际链接。但这是假设情景。我这里只显示2个用户。我的实际数据集中有大约10,000个用户。

链接1 https://www.google.com/all_users

JSON输出

{
            "name": "joe",
            "uri": "/id/UserObject/User/1234-1234",
},
{
            "name": "matt",
            "uri": "/id/UserObject/User/5678-5678",
}

链接2 https://www.google.com//id/UserObject/User/1234-1234

JSON输出

{
            "name": "joe",
            "uri": "/id/UserObject/User/1234-1234",
            "Property Values": {
              "height": "2",
              "location": "canada"
             },
            "Other Values": {
              "work": "google",
              "occupation": "developer"
             }
}

嵌套JSON

{
    "PropertySetClassChildrenResponse": {
        "PropertySetClassChildren": {
            "PropertySetInstances": {
                "totalCount": "1",
                "Elements": [
                    {
                        "name": "SystemObject",
                        "uri": "/type/PropertySetClasses/SystemObject"
                    }
                ]
            }
        }
    }
}

4 个答案:

答案 0 :(得分:1)

未经测试,但这应该指向正确的方向。它使用Promises并假设在ES6环境中运行:

const rp = require('request-promise');
const Promise = require('bluebird');

fetchAllUsers()
.then(extractUserUris)
.then(extractUserIds)
.then(buildUserDetailRequests)
.then(Promise.all) // run all the user detail requests in parallel
.then(allUserData => {
  // allUserData is an array of all users' data
});

function fetchAllUsers() {
  return rp('https://api.whatever.com/all_users');
}

function extractUserUris(users) {
  return users.map(user => user.uri);
}

function extractUserIds(userUris) {
  return userUris.map(userUri => userUri.split('/').pop());
}

function buildUserDetailRequests(userIds) {
  return userIds.map(userId => rp("https://api.whatever.com/user/" + userId));
}

答案 1 :(得分:1)

我建议使用request package来简化您的HTTP请求。

> npm install request

然后您将获得所有用户的列表:

var request = require('request');

request.get({url: "https://example.org/all_users"}, handleUsersResponse);

您可以像这样处理请求响应:

function(err, response, body) {
   if (!err && response.statusCode == 200) {
       // parse json (assuming array of users)
       var users = JSON.parse(body);

       // iterate through each user and obtain user info
       for(var i = 0; i < users.length; i++) {
           var userUri = users[i].uri;
           obtainUserInfo(userUri)
       }
   }
}

obtainUserInfo函数与上面的代码类似。

要记住的一件重要事情是因为HTTP请求是异步进行的,所以当你在循环中发出请求时,循环的下一次迭代不会等到工作完成在移动到下一次迭代并开始下一个请求之前完成。因此,实际上,您的循环将几乎并行地启动所有 HTTP请求。这很容易使客户端和服务器都崩溃。解决此问题的一种方法是使用工作队列将工作排入队列,并确保在任何给定时间仅执行最大数量的HTTP请求。

答案 2 :(得分:1)

您不希望进行同步调用,这违背了使用Node的目的。因此,德克萨斯州投资于我的节点权力,我特此推出了我想你的同步方式!

开玩笑:),但让我们以Node方式做到这一点。 安装这两个库:
sudo npm install Promise
sudo npm install request

并将代码设置为:

var Promise = require('promise');
var request = require('request');
//Get your user data, and print the data in JSON:
getUserData()
  .then(function(userData) {
    console.log(JSON.stringify(userData));
  }).catch(function(err) {
    console.log('Error: ' +err);
  });

/**
 * Prepares an Object containing data for all users.
 * @return Promise - Contains object with all user data.
 */
function getUserData() {
  return new Promise(function(fulfill, reject) {
    // Make the first request to get the user IDs:
    var url1 = 'https://www.google.com/all_users';
    get(url1)
      .then(function(res) {
        res = JSON.parse(res);
        // Loop through the object to get what you need:
        // Set a counter though so we know once we are done.
        var counter = 0; 
        for (x=0; x<res.users.length; x++) {
          var url2 = 'https://www.google.com//id/UserObject/User/';
          url2 = url2 + res.users.id; //Wherever the individual ID is stored.
          var returnDataArr = [];
          get(url2)
            .then(function(res2) {
              // Get what you need from the response from the 2nd URL.
              returnDataArr.push(res2);
              counter++;
              if (counter === res.users.length) {
                fulfill({data: returnDataArr}); //Return/Fulfill an object containing an array of the user data.
              }
            }).catch(function(err) {
              // Catch any errors from the 2nd HTTPS request:
              reject('Error: ' +err);
            });
        }).catch(function(err) {
          // Catch any errors from the 1st HTTPS request:
          reject('Error: ' +err);
  });
}

/**
 * Your HTTPS GET Request Function
 * @param url - The url to GET
 * @return Promise - Promise containing the JSON response. 
 */
 function get(url) {
   return new Promise(function(fulfill, reject) {
     var options = {
       url: url,
       headers: {
       'Header Name': 'Header Value',
       'Accept': 'application/json',
       'Content-Type': 'application/json'
     };
     request(options, function(err, res, body) {
       if (err) {
         reject(err);
       } else {
         fulfill(body);
       }
     });
   });
 }

所以这个Promise做的是,一旦我们实际拥有它,它就 返回 。在上面的代码中,我们首先获取用户列表,然后在解析它时,我们正在创建一个新的异步HTTP请求以获取其上的其他数据。获得用户数据后,我们将其推送到数组。 最后,一旦我们的计数器到达其端点,我们知道我们已经获得了所有用户数据,因此我们调用fulfill,这实质上意味着return,并返回一个包含用户数据数组的对象。

如果这是有道理的,请告诉我。

答案 3 :(得分:0)

上面的答案帮助我进一步完善了我的解决方案并获得了理想的结果。但是,我花了很多时间试图理解节点,节点中的promises,进行API调用等。希望这对初学者级节点开发人员有所帮助。

<强>节点 Node.js®是基于Chrome的V8 JavaScript引擎构建的JavaScript运行时。 Node.js使用事件驱动的非阻塞I / O模型,使其轻量且高效。 Node.js的包生态系统,npm,是世界上最大的开源库生态系统。

如果您是JavaScript开发人员,您更愿意使用Node,因为您不必花时间学习Java或Python等新语言。

<强>目标 对外部链接进行HTTPS调用以获取所有服务器URI。将URI作为参数传递,以创建第二个链接以获取所有服务器属性。循环到所有服务器uris和属性。请参阅顶部的原始帖子以获取数据结构。外部链接还需要基本身份验证和标头。

<强> CODE 安装NPM模块请求(https调用),bluebird(promises)和lodash(实用程序)和express(节点框架)。

/

********************** MODULES/DEPENDENCIES **********************/
var express = require('express');
var request = require('request');
var Promise = require('bluebird');
var _ = require("lodash");

/********************** INITIATE APP **********************/
var app = express();

console.log("Starting node server...");

/**
 * Your HTTPS GET Request Function
 * @param url - The url to GET
 * @return Promise - Promise containing the JSON response. 
 */
function get(url) {
    return new Promise(function(resolve, reject) {

        // var auth = "Basic " + new Buffer(username + ':' + password).toString("base64");

        var options = {
            url: url,
            headers: {
                // 'Authorization': auth,
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            }
        };

        console.log("Calling GET: ", url);

        if ('development' == app.get('env')) {
            console.log("Rejecting node tls");
            process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
        }

        request(options, function(error, response, body) {
            if (error) {
                reject(error);
            } else {
                // console.log("THIS IS BODY: ", body);
                resolve(body);
            }
        });
    });
}

/********************** GET DATA FUNCTION **********************/
function getServerData() {
    /********************** URI VARIABLES **********************/
    var username = 'username',
        password = 'password',
        role = 'Read-Only',
        url_host = 'https://link.com:10843';

    /********************** URL 1 **********************/
    var url1 = url_host + '/type/PropertySetClasses/SystemObject/Server/?maxResults=1000&username=' + username + '&password=' + password + '&role=' + role;

    console.log("Getting server data...", url1);

    /********************** GET REQUEST 1 **********************/
    return get(url1)
    .then(function(res) {

        console.log("Got response!");

        res = JSON.parse(res);
        res = res.PropertySetClassChildrenResponse.PropertySetClassChildren.PropertySetInstances.Elements;

        // console.log("THIS IS RES: ", res);

        /********************** FETCH URI FROM RES NESTED OBJECT **********************/
        var server_ids = _.map(res, function(server) {
            return server.uri;
        });

        console.log("Calling server urls", server_ids);

        // Loop through the object to get what you need:
        // Set a counter though so we know once we are done.

        return Promise.map(server_ids, function (id) {
            var url2 = url_host + id + '?username=' + username + '&password=' + password + '&role=' + role;

            console.log("Calling URL", url2);

            return get(url2)
            .then(function(res2) {
                res2 = JSON.parse(res2);
                var elements = res2.PropertySetInstanceResponse.PropertySetInstance.PropertyValues.Elements;

                console.log("Got second response", res2, elements);
                return elements;
            });
        })
        .then(function (allUrls) {
            console.log("Got all URLS", allUrls);
            return allUrls;
        });
    })
    .catch(function(err) {
        console.error(err);
        throw err;
    });

};

app.listen(8080, function() {
    console.log("Server listening and booted on: " + 8080);

    app.get("/serverInfo", function (req, res) {
        console.log("Calling server info");

        return getServerData()
        .then(function(userData) {
            var userData = JSON.stringify(userData, null, "\t");
            console.log("This is USERDATA Data: ", userData);

            res.send(userData);
        })
        .catch(function(err) {
            console.error(err);

            res.send({
                __error: err,
                message: err.message
            });
        });
    });
});