Node.js使用异步数据进行响应

时间:2014-11-22 20:08:27

标签: javascript node.js asynchronous proxy callback

最近我开始学习一些关于Node.js及其功能的内容,并尝试将其用于某些Web服务。 我想创建一个Web服务,它将作为Web请求的代理。 我希望我的服务以这种方式工作:

  1. 用户将访问我的服务 - > http://myproxyservice.com/api/getuserinfo/tom
  2. 我的服务将执行请求 - > http://targetsite.com/user?name=tom
  3. 响应的数据会反映给用户。
  4. 为了实现它,我使用了以下代码:

    app.js:

    var express = require('express');
    var bodyParser = require('body-parser');
    var app = express();
    
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(bodyParser.json());
    
    var proxy = require('./proxy_query.js')
    
    function makeProxyApiRequest(name) {
      return proxy.getUserData(name, parseProxyApiRequest);
    }
    
    function parseProxyApiRequest(data) {
      returned_data = JSON.parse(data);
      if (returned_data.error) {
        console.log('An eror has occoured. details: ' + JSON.stringify(returned_data));
        returned_data = '';
      }
      return JSON.stringify(returned_data);
    }
    
    app.post('/api/getuserinfo/tom', function(request, response) {
      makeProxyApiRequest('tom', response);
      //response.end(result);
    });
    
    var port = 7331;
    

    proxy_query.js:

    var https = require('https');
    
    var callback = undefined;
    
    var options = {
        host: 'targetsite.com',
        port: 443,
        method: 'GET',
    };
    
    function resultHandlerCallback(result) {
        var buffer = '';
    
        result.setEncoding('utf8');
        result.on('data', function(chunk){
            buffer += chunk;
        });
    
        result.on('end', function(){
            if (callback) {
                callback(buffer);
            }
        });
    }
    
    exports.getUserData = function(name, user_callback) {
        callback = user_callback
        options['path'] = user + '?name=' + name;
    
        var request = https.get(options, resultHandlerCallback);
    
        request.on('error', function(e){
            console.log('error from proxy_query:getUserData: ' + e.message)
        });
    
        request.end();
    }
    app.listen(port);
    

    我希望我没有搞砸这段代码,因为我更换了一些东西以适应我的榜样。

    无论如何,问题是我想在HTTP请求完成时将响应发布给用户而我无法找到如何执行此操作,因为我使用express和express使用异步调用,http请求也是如此。 我知道如果我想这样做,我应该将makeProxyApiRequest传递给响应对象,这样他就可以将它传递给回调,但由于asyn问题,这是不可能的。

    有什么建议吗? 将不胜感激。

1 个答案:

答案 0 :(得分:2)

当您使用函数处理路由处理中的请求时,最好将它们编写为快速中间件函数,获取特定的请求/响应对,并使用express的next级联模型:< / p>

function makeProxyApiRequest(req, res, next) {
  var name = parseProxyApiRequest(req.name);
  res.locals.userdata = proxy.getUserData(name);
  next();
}

function parseProxyApiRequest(req, res, next) {
  try {
    // remember that JSON.parse will throw if it fails!
    data = JSON.parse(res.locals.userdata);
    if (data .error) {
      next('An eror has occoured. details: ' + JSON.stringify(data));
    }
    res.locals.proxyData = data;
    next();
  }
  catch (e) { next("could not parse user data JSON.");  }
}

app.post('/api/getuserinfo/tom',
  makeProxyApiRequest,
  parseProxyApiRequest,
  function(req, res) {
    // res.write or res.json or res.render or
    // something, with this specific request's
    // data that we stored in res.locals.proxyData
  }
);

现在更好的方法是将这些中间件功能移到自己的文件中,这样你就可以做到:

var middleware = require("./lib/proxy_middleware");
app.post('/api/getuserinfo/tom',
  middleware.makeProxyApiRequest,
  middleware.parseProxyApiRequest,
  function(req, res) {
    // res.write or res.json or res.render or
    // something, with this specific request's
    // data that we stored in res.locals.proxyData
  }
);

让您的app.js尽可能小。请注意,客户端的浏览器只会等待快递的响应,这会在res.writeres.jsonres.render等使用后发生。在此之前,浏览器和服务器之间的连接只是保持打开状态,所以如果您的中间件调用需要很长时间,那很好 - 浏览器会愉快地等待时间以获得回复的响应,并将在同一时间做其他事情。

现在,为了得到name,我们可以使用express的参数构造:

app.param("name", function(req, res, next, value) {
  req.params.name = value;
  // do something if we need to here, like verify it's a legal name, etc.
  // for instance:
  var isvalidname = validator.checkValidName(name);
  if(!isvalidname) { return next("Username not valid"); }
  next(); 
});

...

app.post("/api/getuserinfo/:name", ..., ..., ...);

使用此系统,将根据我们使用app.param定义的:name参数来处理任何路线的name部分。请注意,我们不需要多次定义它:我们可以执行以下操作,它们都可以正常工作:

app.post("/api/getuserinfo/:name", ..., ..., ...);
app.post("/register/:name", ..., ..., ... );
app.get("/api/account/:name", ..., ..., ... );

并且对于具有:name的每个路由,“name”参数处理程序的代码将启动。

对于proxy_query.js文件,将其重写为适当的模块可能比使用单个导出更安全:

// let's not do more work than we need: http://npmjs.org/package/request
// is way easier than rolling our own URL fetcher. In Node.js the idea is
// to write as little as possible, relying on npmjs.org to find you all
// the components that you need to glue together. If you're writing more
// than just the glue, you're *probably* doing more than you need to.
var request = require("request");
module.exports = {
  getURL: function(name, url, callback) {
   request.get(url, function(err, result) {
     if(err) return callback(err);
     // do whatever processing you need to do to result:
     var processedResult = ....
     callback(false, processedResult);
   });
  }
};

然后我们可以在中间件中使用proxy = require("./lib/proxy_query");来实际获取URL数据。