编写表达中间件以在正文解析器之前获取原始请求体

时间:2016-11-16 17:00:51

标签: node.js body-parser

我编写了一个Express中间件来从请求中检索原始主体,然后在body-parser中间件之前设置它。

我的自定义中间件正在调用req.setEncoding('utf8'),但这会导致以下正文解析器错误:

  

错误:不应设置流编码

at readStream (/node_modules/body-parser/node_modules/raw-body/index.js:211:17) 
at getRawBody (/node_modules/body-parser/node_modules/raw-body/index.js:106:12)
at read (/node_modules/body-parser/lib/read.js:76:3)
at jsonParser (/node_modules/body-parser/lib/types/json.js:127:5)

这是我的代码:

var express = require('express');
var bodyParser = require('body-parser')

function myMiddleware() {
  return function(req, res, next) {
    req.rawBody = '';
    req.setEncoding('utf8');

    req.on('data', function(chunk) {
      req.rawBody += chunk;
    });

    req.on('end', function() {
      next();
    });
  }
}

var app = express();
app.use(myMiddleware());
app.use(bodyParser.json());

var listener = app.listen(3000, function() {
});

app.get('/webhook/', function (req, res) {
  res.sendStatus(200);
});

有没有办法取消编码?有没有另一种方法来退出原始身体,但仍然使用身体解析器?

3 个答案:

答案 0 :(得分:3)

您正在“完成”内部调用next(),这意味着该流已被使用。相反,为“数据”设置处理程序,然后使用next()传递请求。 “完成”事件很可能在bodyParser内处理,因此在执行后您可以访问req.rawBody。如果不是这种情况,您可以添加另一个在next()内调用req.on('done')的中间件来保持其余的处理,直到您拥有所有数据。

// custom middleware - req, res, next must be arguments on the top level function
function myMiddleware(req, res, next) {
  req.rawBody = '';

  req.on('data', function(chunk) {
    req.rawBody += chunk;
  });

  // call next() outside of 'end' after setting 'data' handler
  next();  
}

// your middleware
app.use(myMiddleware);

// bodyparser
app.use(bodyParser.json())

// test that it worked
function afterMiddleware(req, res, next) {
  console.log(req.rawBody);
  next();  
}

app.use(afterMiddleware);

如果您需要访问原始主体,您可能还需要查看bodyParser.raw()。这会将原始主体放入req.body,与bodyParse.json()相同,但可以根据内容类型有条件地运行 - 请查看options.type

答案 1 :(得分:3)

事实证明,body-parser有一个验证选项,可以在读取请求正文时调用函数。该函数接收主体作为缓冲区。

以下是一个例子:

var express = require('express');
var bodyParser = require('body-parser')

function verifyRequest(req, res, buf, encoding) {
  // The raw body is contained in 'buf'
  console.log( buf.toString( encoding ) );
};

var app = express();
var listener = app.listen(3000);

// Hook 'verifyRequest' with body-parser here.
app.use(bodyParser.json({ verify: verifyRequest }))

app.post('/webhook/', function (req, res) {
  res.status(200).send("done!");
});

答案 2 :(得分:0)

我推荐一种不同的方法,因为你当前的方法实际上消耗了这个消息并且使得body-parser无法读取它(并且通过同步调用next会产生一堆边缘案例错误) :

app.use(bodyParser.json());
app.use(bodyParser.text({type: '*/*'}));

这将读取任何application/json请求作为JSON,其他所有内容作为文本。

如果除文本外还必须有JSON对象,我建议您自己解析:

app.use(bodyParser.text({type: '*/*'}));
app.use(myMiddleware);

function myMiddleware(req, res, next) {
    req.rawBody = req.body;
    if(req.headers['content-type'] === 'application/json') {
        req.body = JSON.parse(req.body);
    }
    next();
}