【问题标题】:Express/Connect: Unable to parse body after making call to redisExpress/Connect:调用 redis 后无法解析正文
【发布时间】:2013-05-10 22:05:36
【问题描述】:

我试图通过仅根据请求的路径调用某些中间件函数来缩短我的 Express/Connect 中间件管道。

但是,以下操作会失败:

_cookieParser(req, res, function(err) {
     if(err) return next(err);
     _session(req, res, function(err) {
         if(err) return next(err);
         _csrf(req, res, function(err) {
              if(err) return next(err);
              loadUserFromSession(req, res, function(err) {
                   if(err) return next(err);
                   if(req.method == "POST") {
                       _bodyParser(req, res, next);
                   } else {
                       next();
                   }
              });
          });
     });
});

但这会很好用:

_cookieParser(req, res, function(err) {
     if(err) return next(err);
     _session(req, res, function(err) {
         if(err) return next(err);
         _csrf(req, res, function(err) {
              if(err) return next(err);
              _bodyParser(req, res, function(err) {
                   if(err) return next(err);
                   loadUserFromSession(req, res, next);
              });
          });
     });
});

loadUserFromSession 在哪里:

function loadUserFromSession(req, res, next) {
    if(req.session && req.session.userId) {
        userFunctions.getUserById(req.session.userId, function(err, user) {
            if(err) return next(err);
            if(user) {
                req.user = user;
                return next();
            } else {
                req.session.destroy();
                return next(new Error('Unauthenticated'));
            }
        });
    } else {
        return next(new Error('Unauthenticated'));
    }        
};

为什么我不能在 loadUserFromSession() 之后调用 bodyParser()?

编辑

抱歉没有详细说明失败/意外结果。

如果我在 loadUserFromSession() 之后放置 bodyParser() 或只是 json()(因为 POST 内容是 json),则调用永远不会在 json() 内返回。如果我在 res.on('data') 或 res.on('end') 上的节点检查器中放置断点,都不会被触发。

json中间件的源码如下:

exports = module.exports = function(options){
  var options = options || {}
    , strict = options.strict !== false;

  var limit = options.limit
    ? _limit(options.limit)
    : noop;

  return function json(req, res, next) {
    if (req._body) return next();
    req.body = req.body || {};

    if (!utils.hasBody(req)) return next();

    // check Content-Type
    if ('application/json' != utils.mime(req)) return next();

    // flag as parsed
    req._body = true;

    // parse
    limit(req, res, function(err){
      if (err) return next(err);
      var buf = '';
      req.setEncoding('utf8');
      req.on('data', function(chunk){ 
        buf += chunk                  <==BREAKPOINT NEVER GETS CALLED
      });
      req.on('end', function(){
        var first = buf.trim()[0];    <==BREAKPOINT NEVER GETS CALLED

        if (0 == buf.length) {
          return next(400, 'invalid json, empty body');
        }

        if (strict && '{' != first && '[' != first) return next(400, 'invalid json');
        try {
          req.body = JSON.parse(buf, options.reviver);
          next();
        } catch (err){
          err.body = buf;
          err.status = 400;
          next(err);
        }
      });
    });
  }
};

【问题讨论】:

  • 请编辑您的问题并将“将失败”替换为实际的错误消息和堆栈跟踪,或者如果失败不是错误,请描述错误行为。
  • 旁注,使用中间件的嵌套方式较少。您上面所拥有的对于 express 来说是唯一的(礼貌地说)。如果您只想在某些路径中使用某些东西,请尝试app.use('/some/path', someMiddleware),但实际上大多数中间件在不适用于当前请求时成本非常小。例如,bodyParser 只检查请求方法,如果没有正文,则立即调用next()。这不是你不应该尝试解决的问题。
  • @PeterLyons 添加了有关我的问题的更多详细信息。在结构上,我完全不同意你的看法。我走这条路,不是因为性能,而是安全。我不希望我的应用程序上传所有可以发布的文件。我有一些端点是公开的,有些不是。我不希望仅仅因为人们将文件上传到不应允许上传的端点而使我的应用程序超载(或 DDOS,如果它被知道的话)。

标签: express connect middleware


【解决方案1】:

好的,考虑这个建议/代码审查而不是具体答案,但希望这会有所帮助。

首先,我的猜测是理解以下内容将解决您的问题并使您不再困惑,尽管我不能确切说明为什么上面的第二个示例与第一个示例的行为不同,因为您的孤立的 sn-ps,但是基于此信息:对于给定的请求,一系列事件(dataend 等)将触发一次且仅触发一次。如果在触发时未附加侦听器,则永远不会调用该侦听器。如果侦听器在触发后被附加,它将永远不会被调用。 bodyParser 的代码可以避免多次尝试重新解析同一个请求正文(因为必要的事件永远不会触发,并且代码会挂起而不响应请求)。

所以,我怀疑在您的应用程序中您有 app.use(express.bodyParser()) 并且该中间件在您上面的自定义内容之前被调用并运行。因此,我的建议(也解决了您的文件上传安全问题)是:

不要像在示例中看到的那样全局安装bodyParserapp.use(express.bodyParser())。这段代码对于第 1 天的示例应用程序来说很简洁,但对于生产站点来说完全不合适。您应该改为:

app.post('/upload/some/file', express.bodyParser(), myUploadHandler);

只需在需要的地方使用正文解析器,别无他处。不要再深入你那堆奇怪的嵌套中间件。使用内置在连接中的中间件堆栈设计,有多种简洁的方法可以在 HARMONY 中获得所需的功能、安全性和效率。一般来说,您可以在不同的路径上使用不同的中间件堆栈,并且可以将 express/connect 配置为在逐个路径的基础上很好地匹配您想要发生的事情。

【讨论】:

  • 这是我先尝试的;我希望避免在每条路由上进行 2/3/4 次中间件调用,尤其是在它们重复的情况下;因此我上面的方法。
猜你喜欢
  • 2015-07-18
  • 2019-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-28
  • 2019-11-04
  • 1970-01-01
  • 2014-06-08
相关资源
最近更新 更多