Expressjs原始请求体

69

我该如何访问由expressjs提供的请求对象的原始数据?

var express = require('./node_modules/express');
var app = express.createServer();
app.post('/', function(req, res)
{
    console.log(req.body); //says 'undefined'
});
app.listen(80);

这里稍微多一点的上下文会更有用。您期望在正文中收到什么类型的数据?您是想读取表单数据还是更复杂的内容? - loganfsmyth
4
我不确定我理解那会如何影响我获取原始数据的方式。假设那只是一些未知的二进制数据。 - Andrey Kon
4
同时,你只需要执行require('express')即可。它会自动找到该模块。 - loganfsmyth
1
但是如果数据是JSON或XML呢? - Andrey Kon
1
Tarandeep Gill,你不会相信我做了什么,哈哈,但我刚刚修改了bodyParser.js源代码,并带回了那行代码rawBody :) 所以现在我有了它。虽然对我来说似乎不太对,因为在这种情况下,我的代码只调用bodyParser来获取这个rawBody,这会导致执行很多我不需要的代码。从性能角度来看,这是不正确的。 - Andrey Kon
显示剩余3条评论
12个回答

62

应该可以使用类似这样的代码:

var express = require('./node_modules/express');
var app = express.createServer();
app.use (function(req, res, next) {
    var data='';
    req.setEncoding('utf8');
    req.on('data', function(chunk) { 
       data += chunk;
    });

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

app.post('/', function(req, res)
{
    console.log(req.body);
});
app.listen(80);

5
你建议我做跟 ExpressJs 已经做过的一样的工作!一定有办法从请求中获取到那个可恶的原始主体。 - Andrey Kon
7
bodyParser() 中间件曾经有 .rawBody,但已被移除:https://github.com/senchalabs/connect/commit/c3170eee8cd60c92bcccca6054c1ebbb93a1a821#diff-0 - stewe
1
是的。那么没有办法获取原始主体吗? - Andrey Kon
2
@AndreyKon 不是的。这通常不是你想要的。Node非常专注于尽可能使用流。 - loganfsmyth
5
在 Express 4 中,似乎这个解决方案不再起作用了。 - Emanuele Casadio
显示剩余4条评论

51
使用bodyParser.text()中间件可以将文本主体放入req.body中。
app.use(bodyParser.text({type: '*/*'}));

如果您想将文本正文的处理限制在特定路由或文章内容类型上,也可以这样做。

app.use('/routes/to/save/text/body/*', bodyParser.text({type: 'text/plain'})); //this type is actually the default
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
如果您需要一个原始的Buffer,您可以使用bodyParse.raw()
app.use(bodyParser.raw({type: '*/*'}));
注意:本答案是在 node v0.12.7、express 4.13.2 和 body-parser 1.13.3 版本进行测试的。

2
这对我很有用。我需要处理XML主体,所以我使用了以下行:app.use(bodyParser.text({ type: '*/xml' })); 这样可以很好地填充我的req.body。 - romiem
3
这应该是正确答案。其余的已经过时。 - keithics
app.use(bodyParser.raw({type: '*/*'})); 如果您想要二进制数据作为缓冲区。 - Bernardo Ramos
这个答案还是有点令人困惑(但我还是点了赞,因为它还是有帮助的)。代码示例使用了 text(),但你提到要使用 raw() - Matt Browne
是哪一个,text() 还是 raw() - korona
抱歉,raw() 返回一个缓冲区,text() 解析为文本并返回一个字符串。对于这个特定的问题,他们想要使用 text(),我会更新答案。 - Aaron Silverman

29
将以下中间件放在bodyParser中间件之前。它将收集请求的原始消息体数据并存储在request.rawBody中,不会干扰bodyParser中间件的正常工作。
app.use(function(req, res, next) {
    var data = '';
    req.setEncoding('utf8');
    req.on('data', function(chunk) { 
        data += chunk;
    });
    req.on('end', function() {
        req.rawBody = data;
        next();
    });
});
app.use(express.bodyParser());

2
我认为你需要把 next() 调用移到 req.on('end' 回调函数内部。 - TechplexEngineer
1
正如@user1144642所指出的那样,原始代码是正确的。这是基于事件的代码,如果您在此处阻止执行并消耗所有数据,则bodyParser将无法获取任何数据并永远挂起。外部的next()将控制权传递给bodyParser,从而允许它绑定事件处理程序并同时接收数据。 - pronebird
1
只有在我将next()移动到req.on('end', ...)之外时,这才对我起作用。 - Tobi
@Tobi 你使用最新的Express吗? - pronebird
app.use(function(req, res, next) { var req.rawData = ''; req.setEncoding('utf8'); req.on('data', function(chunk) { req.rawData += chunk; }); next(); }); app.use(express.bodyParser()); - Daniele Cruciani
显示剩余6条评论

22

默认情况下,express不会缓存数据,除非您添加中间件来执行此操作。简单的解决方案是遵循下面@Stewe答案中的示例,手动合并所有数据,例如:

var concat = require('concat-stream');
app.use(function(req, res, next){
  req.pipe(concat(function(data){
    req.body = data;
    next();
  }));
});

这样做的缺点是您现在将所有POST请求的内容移动到RAM中作为一个连续的块,这可能并不必要。另一个值得考虑的选项取决于您需要处理多少数据的post请求体,可以考虑将数据作为流进行处理。

例如,对于XML数据,您可以使用支持按块解析XML的XML解析器之一,如XML Stream。您可以像这样执行:

var XmlStream = require('xml-stream');

app.post('/', function(req, res) {
  req.setEncoding('utf8');
  var xml = new XmlStream(req);
  xml.on('updateElement: sometag', function(element) {
    // DO some processing on the tag
  });
  xml.on('end', function() {
    res.end();
  });
});

3
朋友,我们在这里是为了帮助你,没有必要表现得如此恼怒。盲目地缓存数据是没有意义的,如果你真正花时间学习Node.js是很容易做到的。这里的每个答案都解释了如何实现。 - loganfsmyth
4
我对bodyParser的行为感到不满,它会收集请求体数据,但如果这些数据无法被解析,则不会将它们传递给我。这种行为是不合逻辑的。我认为,在使用中间件框架时,应该隐藏处理流的细节,让开发者专注于业务逻辑。 - Andrey Kon
1
bodyParser只收集与其允许的内容类型匹配的块,因此它不应该失败。如果实际上出现解析器错误,则表示您正在使用错误的内容类型。如果您有自定义内容类型,则应添加适用于该内容类型的自定义解析器中间件,而不是篡改bodyParser中间件。 - loganfsmyth
1
自从 Express 4 和 body-parser 1.15.2 版本以后,你可以将 req.body 转换成一个 buffer。请参考这个答案 - DJDaveMark
1
@rofls 你是否忘记了这段代码中的 var concat = require('concat-stream'); 部分? - loganfsmyth
显示剩余2条评论

16
app.use(bodyParser.json({
    verify: function (req, res, buf, encoding) {
        req.rawBody = buf;
    }
}));
app.use(bodyParser.urlencoded({
    extended: false,
    verify: function (req, res, buf, encoding) {
        req.rawBody = buf;
    }
}));

1
太棒了,非常感谢!(注意:这里使用了body-parser模块) - scape
1
谢谢,这也帮了我很多!这是最佳答案。 :-) - Joel
我使用了这个 https://flaviocopes.com/express-get-raw-body/ 来解决它,但是我投票支持这个答案,因为它是同样的东西。 - Nassim

9

看起来Express的bodyParser只会解析传入数据,如果content-type被设置成以下任意一种情况:

  1. application/x-www-form-urlencoded
  2. application/json
  3. multipart/form-data

在其他所有情况下,它甚至都不会读取数据。

你可以将express/node_modules/connect/lib/middleware/bodyParser.js的第92行更改为:

} else {
        next();
}

致:

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

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

然后,在你的代码中读取req.rawBody


2
修改connect并不是一个很好的方法,虽然它可以工作。正如@Stewe的答案所示,你可以很容易地添加自己的中间件,这样你就不必修改任何外部库了。 - loganfsmyth

6

如果您想将正文内容作为缓冲区:

var rawParser = function(req, res, next) {
    var chunks = [];
    req.on('data', function(chunk) { 
        chunks.push(chunk)
    });
    req.on('end', function() {
        req.body = Buffer.concat(chunks);
        next();
    });
}

或者

var rawParser = bodyParser.raw({type: '*/*'});

然后:

app.put('/:name', rawParser, function(req, res) {
  console.log('isBuffer:', Buffer.isBuffer(req.body));
})

对于所有的路由:

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

5

看起来现在这变得容易多了

现在,body-parser模块可以解析原始数据和文本数据,这使得任务成为一个一行代码

app.use(bodyParser.text({type: 'text/plain'}))

或者

app.use(bodyParser.raw({type: 'application/binary'}))

这两行代码只是填充了body属性,因此可以使用res.body获取文本。

bodyParser.text()将给你UTF8字符串,而bodyParser.raw()将给你原始数据。

这是纯文本数据的完整代码:

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

app.use(bodyParser.text({type: 'text/plain'}))

app.post('/', function (req, res, next) {
    console.log('body:\n' + req.body)

    res.json({msg: 'success!'})

    next()
})

完整文档请参阅: https://www.npmjs.com/package/body-parser

我使用了express 4.16和body-parser 1.18。


5

所有的答案都已过时,如果任何人仍在苦苦挣扎,那么Express内置了中间件Express raw middleware

这个中间件在 Express v4.16.0 及以上版本中可用。 这是一个内置于 Express 中的中间件函数。它解析带有 JSON 负载的传入请求,并基于 body-parser 实现。

var express = require("express");
var app = express();

app.use(express.raw({ type: "*/*" }))

app.post("/", (req, res) => {
  // req.body = JSON.parse(req.body); // To parse JSON if needed (in-case)
  console.log(req.body);
  res.end();
});

app.listen(3000, (err) => {
if(!err) console.log("App running!!")
});

这是否类似于bodyParser.raw([options])? - Pedro Henrique

4
如果你遇到以上解决方案干扰正常的POST请求的问题,可以尝试类似以下的方法:
app.use (function(req, res, next) {
    req.rawBody = '';
    req.setEncoding('utf8');
    req.on('data', function(chunk) { req.rawBody += chunk });
});

更多信息和资源请参考:https://github.com/visionmedia/express/issues/897#issuecomment-3314823

6
你忘记在结尾处加上 next(); 了。在加上这行代码后,它对我有效了。 - Maxime Fafard

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