【问题标题】:Node.js respond with asynchronous dataNode.js 使用异步数据响应
【发布时间】:2015-01-20 19:32:28
【问题描述】:

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

  1. 用户将访问我的服务 -> http://myproxyservice.com/api/getuserinfo/tom
  2. 我的服务将执行请求 -> http://targetsite.com/user?name=tom
  3. 响应的数据会反映给用户。

为了实现它,我使用了以下代码:

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 传递给响应对象,这样他就可以将它传递给回调,但由于异步问题,这是不可能的。

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

【问题讨论】:

  • 我注意到你的 makeProxyApiRequestparseProxyApiRequest 函数都被阻塞了。对于这两种情况,我强烈建议遵循 Node 的 function name(input, callbackfunction) 模式,当一切正常时,回调在您的函数中调用为 callback(false, result),或者当出现问题时调用为 callback(someerrorobjectorstring)
  • 谢谢,这是个好主意。但这并不能解决问题。你有什么建议吗?
  • 给你写一个答案,但要记住的一点是,用户的请求在他们的浏览器和他们正在联系的服务器之间是总是异步的,所以这实际上并不是一个问题。 express 迫使您异步形成响应的事实实际上是一个好处,因此您不会想要同步地做所有事情。

标签: javascript node.js asynchronous proxy callback


【解决方案1】:

当您在路由处理中使用函数来处理请求时,最好将它们编写为 express 中间件函数,采用特定的请求/响应对,并利用 express 的 next 级联模型:

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 等时发生。在那之前,浏览器和服务器之间的连接只是保持打开状态,所以如果你的中间件调用需要很长时间,那很好 - 浏览器会很高兴地等待一个 long 时间来让响应被发回,同时还会做其他事情。

现在,为了得到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", ..., ..., ...);

使用这个系统,任何路由的:name 部分都将根据我们使用app.param 定义的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 数据获取。

【讨论】:

  • 很好的答案,非常感谢!似乎它解决了我的问题。现在打算实施。非常感谢您的帮助!
  • 嗨,迈克,我试图实现你上面所说的。我得到了一般概念,但我无法弄清楚的事情很少。您在 makeProxyApiRequest 中写道: var name = parseProxyApiRequest(req.name);这个 req.name 来自哪里?谁提供它?对于另一个问题,通过 proxy.getUserData(name);你的意思是proxy.getURL?如果是这样,您在哪里提供回调以及如果结果是异步的,如何将结果分配给变量?你明白我的问题了吗?
  • 让我将这部分添加到答案中,因为我们如何得到这实际上真的很酷。
  • 以及如何执行此行“res.locals.userdata = proxy.getUserData(name);”如果 getUserData(即 getURL)是异步的?
  • 弄清楚了:function makeProxyRequest(req, res, next) { // var name = module.exports.parseProxyRequest(req.name); http.getURL('google.com', function(data){ res.locals.userData = data.body; next(); }); }
猜你喜欢
  • 1970-01-01
  • 2022-01-16
  • 2016-02-14
  • 2021-08-09
  • 1970-01-01
  • 2018-09-04
  • 2015-10-13
  • 2012-08-13
  • 2013-11-22
相关资源
最近更新 更多