Node.js - 使用Express获取原始请求体

117

当我使用Express时,我的代码如下:

app.use(express.bodyParser());

我该如何获取原始请求正文

10个回答

111

编辑2: body parser 模块的1.15.2版本引入原始模式,它返回请求体作为Buffer。默认情况下,它还会自动处理deflate和gzip解压缩。使用示例:

var bodyParser = require('body-parser');
app.use(bodyParser.raw(options));

app.get(path, function(req, res) {
  // req.body is a Buffer object
});

默认情况下,options对象具有以下默认选项:

var options = {
  inflate: true,
  limit: '100kb',
  type: 'application/octet-stream'
};

如果您想让您的原始解析器解析除了application/octet-stream以外的其他MIME类型,则需要在此处进行更改。它还将支持通配符匹配,例如*/**/application


注意:以下答案适用于Express 4之前的版本,在该版本中,中间件仍然与框架捆绑在一起。现代等价物是body-parser模块,必须单独安装。

Express中的rawBody属性曾经可用,但自版本1.5.1以来已被删除。要获得原始请求正文,必须在使用bodyParser之前添加一些中间件。您也可以在此处阅读有关它的GitHub讨论。

app.use(function(req, res, next) {
  req.rawBody = '';
  req.setEncoding('utf8');

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

  req.on('end', function() {
    next();
  });
});
app.use(express.bodyParser());

该中间件将从实际数据流中读取数据,并将其存储在请求的rawBody属性中。然后,您可以像这样访问原始正文:

app.post('/', function(req, res) {
  // do something with req.rawBody
  // use req.body for the parsed body
});

编辑: 看起来这个方法和bodyParser无法共存,因为其中一个会在另一个之前消耗请求流,导致后面的那个永远不会触发 end,从而不调用next(),使你的应用程序挂起。

最简单的解决办法很可能是修改bodyParser的源代码,你可以在Connect的JSON解析器的第57行找到它。这是修改后的版本。

var buf = '';
req.setEncoding('utf8');
req.on('data', function(chunk){ buf += chunk });
req.on('end', function() {
  req.rawBody = buf;
  var first = buf.trim()[0];
  ...
});

你可以在此位置找到该文件:

/node_modules/express/node_modules/connect/lib/middleware/json.js


看起来我们不能这样做。如果我添加了这些代码,\node_modules\express\node_modules\connect\lib\middleware\urlencoded.js 中的事件将不会被触发。req.on("data"),req.on("end") 不会被触发 - haitao_wu
在添加了您的代码后,我使用以下代码处理我的POST请求:app.post("/ajax", function(req,res) { res.send('hello world,post'); } ); 当我的请求内容类型为application/x-www-form-urlencoded时,服务器将不会响应“hello world,post”。 - haitao_wu
我的情况下文件位置不正确。在我的express (>4.0.0)的node_modules中没有connect模块,我还没有找到新的位置。 - Sam Vloeberghs
Connect在Express 4中不是一个依赖项,因此不包含Body Parser模块。如果您仍需要它,可以在这里找到它。 - hexacyanide
1
@hexacyanide,能否请您更新您的答案,并包含对最新body-parser中间件的引用?该模块现在包括raw-body-parser中间件。这对于寻找获取原始请求体的方法的用户非常有帮助。 - eAbi
express.raw 在使用 Google Cloud Tasks 时无法正常工作,因为 Tasks 没有在 http 任务中发送 content-type 头。我能够手动指定头并从那里使其工作。返回了一个缓冲区,我使用一些自定义中间件将其转换为 JSON。 - knownasilya

56

我有一个解决方案,可以与 bodyParser 很好地配合使用,使用 verify 回调函数。在这段代码中,我将其用于获取内容的 sha1 并获取原始正文。

app.use(bodyParser.json({
    verify: function(req, res, buf, encoding) {

        // sha1 content
        var hash = crypto.createHash('sha1');
        hash.update(buf);
        req.hasha = hash.digest('hex');
        console.log("hash", req.hasha);

        // get rawBody        
        req.rawBody = buf.toString();
        console.log("rawBody", req.rawBody);

    }
}));

我对Node.js和express.js都很新(从昨天开始,真的!),所以我想听听对这个解决方案的评论。


5
我非常喜欢这个解决方案。我只是加上了req.rawBody = buf.toString();并从verify函数中去掉了其余的部分,因为那就是我所需要的全部内容,它很好地发挥了作用。不需要更改bodyParser源代码! - Greg
+1 但我的问题现在是我需要一个异步函数来验证是否先前已发送此请求:/ - Renato Gama
3
非常好。我可以建议使用req.rawBody = buf.toString(encoding); - shaharsol
6
这将仅捕获 application/json 请求。 - Pavel Evstigneev

54

这个解决方案对我有效:

var rawBodySaver = function (req, res, buf, encoding) {
  if (buf && buf.length) {
    req.rawBody = buf.toString(encoding || 'utf8');
  }
}

app.use(bodyParser.json({ verify: rawBodySaver }));
app.use(bodyParser.urlencoded({ verify: rawBodySaver, extended: true }));
app.use(bodyParser.raw({ verify: rawBodySaver, type: '*/*' }));

当我使用带有req.on('data', function(chunk) { });的解决方案时,它无法处理分块请求主体。


1
这样做可以处理请求的各个部分中所有主要的数据情况。 - TWright
3
我正在尝试验证Shopify应用Webhook的HMAC,并且这对我起作用了。我大致遵循了这个示例:https://gist.github.com/andjosh/5c4f0244914adfd312e4。 - Chad Johnson
这样可以确保在指定为'application/octet-stream'的content-type时,您仍然可以将req.body作为缓冲区读取,这在处理JSON提交和其他二进制文件提交时非常简单直接。 - Jason Chiang
这个解决方案对我有效,但对于多部分请求无效,因此我们需要删除下面这行代码。app.use(bodyParser.raw({ verify: rawBodySaver, type: '/' })); - undefined

30

小心那些其他答案,如果你想支持json、urlencode等格式,它们可能无法与bodyParser正常配合使用。为了使它能够与bodyParser一起工作,您应该将处理程序条件化,仅在您关心的Content-Type头部注册,就像bodyParser本身所做的那样。

要将请求的原始内容(Content-Type: "text/plain")放入req.rawBody中,您可以执行以下操作:

app.use(function(req, res, next) {
  var contentType = req.headers['content-type'] || ''
    , mime = contentType.split(';')[0];

  if (mime != 'text/plain') {
    return next();
  }

  var data = '';
  req.setEncoding('utf8');
  req.on('data', function(chunk) {
    data += chunk;
  });
  req.on('end', function() {
    req.rawBody = data;
    next();
  });
});

3
我尝试了上面的一个解决方案,然后我的所有GET和JSON POST请求都失败了。上面的解决方案在技术上是正确的,但如果你处理更多种形式的数据并且需要处理更多样化的请求,你需要这个。 - user773737
这里的数据是什么?它是我们从UI发送的变量吗? - Saras Arya
app.use(bodyParser.urlencoded({limit: '80mb', extended: true})); app.use(bodyParser.json({limit: '80mb'})); app.use(bodyParser.raw({type: 'application/octet-stream'}))这段代码是用于在应用程序中使用body-parser中间件,它可以解析HTTP请求的主体,并将其转换为JavaScript对象。其中,urlencoded方法用于解析表单数据,json方法用于解析JSON格式的数据,raw方法用于解析二进制数据。此外,通过设置limit属性,可以限制请求主体的大小。 - Soumya Kanti

21

在2022年

获取每个 API 中的原始主体的最佳方法是将缓冲区转换为字符串。

输入图像描述

app.use(
  express.json({
    limit: '5mb',
    verify: (req, res, buf) => {
      req.rawBody = buf.toString();
    },
  })
);

3
优雅且适合我。无需中间件,也不会干扰其他请求对象。 - JacobRossDev
2
同意,这对修复条纹 Webhook 集成非常有效! - Joseph Astrahan
1
这结束了多日的搜索解决在authorize.net webhook集成中匹配哈希值的问题。谢谢。 - romeplow

15

这是对hexacyanide上面答案的一种变化。这个中间件也处理“data”事件,但在调用“next”之前不等待数据被消耗。这样,这个中间件和bodyParser可以并存,同时并行消耗流。

app.use(function(req, res, next) {
  req.rawBody = '';
  req.setEncoding('utf8');

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

  next();
});
app.use(express.bodyParser());

2
这似乎在长文本上无法正常工作,会提前截断。 - Adam Lockhart
完美地工作了,救了我。谢谢你。 - hakazvaka
我确认这也适用于大文件。我尝试发送了一个1.5MB的文本文件,整个数据都被正确接收。谢谢。 - ATOzTOA
@AdamLockhart - 你的请求被截断时有多大? - UpTheCreek
@UpTheCreek,好久不见了。我不确定。我的最新代码没有使用这个片段,但如果其他人报告没有问题,那么可能是已经修复的错误。 - Adam Lockhart
1
整个请求将在 req.on('end', ... ) 中。如果您想要完整的请求,您需要等待直到那时。 - Chris Cinelli

2
// Change the way body-parser is used
const bodyParser = require('body-parser');

var rawBodySaver = function (req, res, buf, encoding) {
  if (buf && buf.length) {
    req.rawBody = buf.toString(encoding || 'utf8');
  }
}
app.use(bodyParser.json({ verify: rawBodySaver, extended: true }));




// Now we can access raw-body any where in out application as follows
request.rawBody;

-1

我已经解决了向头部属性添加的问题。

{"Content-Type": "application/json"}

-2

使用

app.use(express.json());

或者

app.use(express.text());

或者

app.use(express.urlencoded());

根据您的原始格式。

13
这些东西与原始请求主体完全相反。 - Brad

-4
使用 body-parser 对请求体进行解析:

app.use(bodyParser.text());

app.use(bodyParser.urlencoded());

app.use(bodyParser.raw());

app.use(bodyParser.json());

例如,如果您应该获取原始文本文件,请运行.text()

这是目前body-parser支持的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接