如何从HTTP POST
方法发送的表单数据 (form[method="post"]
) 和文件上传中提取数据,在Node.js中实现?
我已经阅读了文档,进行了谷歌搜索,但没有发现相关信息。
function (request, response) {
//request.post????
}
有没有库或技巧可用?
querystring
模块:var qs = require('querystring');
function (request, response) {
if (request.method == 'POST') {
var body = '';
request.on('data', function (data) {
body += data;
// Too much POST data, kill the connection!
// 1e6 === 1 * Math.pow(10, 6) === 1 * 1000000 ~~~ 1MB
if (body.length > 1e6)
request.connection.destroy();
});
request.on('end', function () {
var post = qs.parse(body);
// use post['blah'], etc.
});
}
}
比如说,如果你有一个名称为age
的input
字段,你可以使用变量post
来访问它:
console.log(post.age);
var POST = qs.parse(body); // use POST
只有像我这样的新手才需要注意:当输入文本字段的名称为“user”时,POST.user
将显示该字段的数据。例如:console.log(POST.user);
- Michael Moellerreadable
回调函数来代替构建在请求体字符串中的数据。一旦触发,请求体通过request.read()
方法可用。 - Thomas Fankhauser<input id="fileupload" type="file" name="files[]" multiple>
。在这里我们如何使用 qs.parse(body)
检索文件数据。当我尝试使用 qs
时,它返回的内容为 {"-----------------------------189644229932\r\nContent-Disposition: form-data;.... -189644229932--\r\n":""}
。如何在纯Node.js中处理提交表单文件数据? - Justin JohnHTML:
<form method="post" action="/">
<input type="text" name="user[name]">
<input type="text" name="user[email]">
<input type="submit" value="Submit">
</form>
API客户端:
fetch('/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
user: {
name: "John",
email: "john@example.com"
}
})
});
Node.js:(自Express v4.16.0以来)
// Parse URL-encoded bodies (as sent by HTML forms)
app.use(express.urlencoded());
// Parse JSON bodies (as sent by API clients)
app.use(express.json());
// Access the parse results as request.body
app.post('/', function(request, response){
console.log(request.body.user.name);
console.log(request.body.user.email);
});
Node.js:(适用于Express <4.16.0)
const bodyParser = require("body-parser");
/** bodyParser.urlencoded(options)
* Parses the text as URL encoded data (which is how browsers tend to send form data from regular forms set to POST)
* and exposes the resulting object (containing the keys and values) on req.body
*/
app.use(bodyParser.urlencoded({
extended: true
}));
/**bodyParser.json(options)
* Parses the text as JSON and exposes the resulting object on req.body.
*/
app.use(bodyParser.json());
app.post("/", function (req, res) {
console.log(req.body.user.name)
});
app.use(express.bodyParser());
之前,这个方法对我没有起作用。 - pettys当http.createServer的回调被调用时,服务器实际上已经接收到了请求的所有头信息,但是数据可能还没有接收到,所以我们需要等待它。http请求对象(一个http.IncomingMessage实例)实际上是一个可读流(readable)(stream)。在可读流中,每当有一块数据到达时,就会发出data
事件(假设您已注册回调),当所有块都到达时,会发出end
事件。以下是如何监听这些事件的示例:
http.createServer((request, response) => {
console.log('Now we have a http message with headers but no data yet.');
request.on('data', chunk => {
console.log('A chunk of data has arrived: ', chunk);
});
request.on('end', () => {
console.log('No more data');
})
}).listen(8080)
如果你尝试这样做,你会注意到这些块是缓冲区。如果你不处理二进制数据,需要使用字符串来工作,我建议使用request.setEncoding方法,它会导致流发出用给定编码解释的字符串,并正确地处理多字节字符。
现在你可能不对每个块都感兴趣,所以在这种情况下,你可能想要像这样进行缓冲:
http.createServer((request, response) => {
const chunks = [];
request.on('data', chunk => chunks.push(chunk));
request.on('end', () => {
const data = Buffer.concat(chunks);
console.log('Data: ', data);
})
}).listen(8080)
这里使用了Buffer.concat,它会简单地连接所有的缓冲区并返回一个大缓冲区。你也可以使用concat-stream模块来完成同样的操作:
const http = require('http');
const concat = require('concat-stream');
http.createServer((request, response) => {
concat(request, data => {
console.log('Data: ', data);
});
}).listen(8080)
如果您正在尝试接受没有文件的HTML表单POST提交或处理默认内容类型的jQuery ajax调用,则内容类型为application/x-www-form-urlencoded
,编码为utf-8
。 您可以使用querystring模块来反序列化它并访问属性:
const http = require('http');
const concat = require('concat-stream');
const qs = require('querystring');
http.createServer((request, response) => {
concat(request, buffer => {
const data = qs.parse(buffer.toString());
console.log('Data: ', data);
});
}).listen(8080)
http.createServer((request, response) => {
request.pipe(fs.createWriteStream('./request'));
}).listen(8080)
正如其他答案所指出的,要记住恶意客户端可能会向您发送大量数据以使您的应用程序崩溃或填满内存,因此为了保护您的应用程序,请确保丢弃超过一定限制的请求。如果您不使用库来处理传入的数据。我建议使用类似stream-meter的东西,如果达到指定的限制,它可以中止请求:
limitedStream = request.pipe(meter(1e7));
limitedStream.on('data', ...);
limitedStream.on('end', ...);
或者
request.pipe(meter(1e7)).pipe(createWriteStream(...));
或者
concat(request.pipe(meter(1e7)), ...);
虽然我已经描述了如何使用HTTP请求体来缓冲和解析内容,但是我建议使用以下这些模块之一而不是自己实现,因为它们可能会更好地处理边缘情况。对于express,我建议使用body-parser。对于koa,有一个类似的模块。
如果您不使用框架,则body非常好。
request
被重复使用,导致request.on('end')
被多次调用了吗?我该如何避免这种情况? - Yan King Yin确保如果有人试图将你的 RAM 淹没,要终止连接!
var qs = require('querystring');
function (request, response) {
if (request.method == 'POST') {
var body = '';
request.on('data', function (data) {
body += data;
// 1e6 === 1 * Math.pow(10, 6) === 1 * 1000000 ~~~ 1MB
if (body.length > 1e6) {
// FLOOD ATTACK OR FAULTY CLIENT, NUKE REQUEST
request.connection.destroy();
}
});
request.on('end', function () {
var POST = qs.parse(body);
// use POST
});
}
}
这是一个非常简单的不依赖框架的包装器,它基于这里发布的其他答案和文章:
var http = require('http');
var querystring = require('querystring');
function processPost(request, response, callback) {
var queryData = "";
if(typeof callback !== 'function') return null;
if(request.method == 'POST') {
request.on('data', function(data) {
queryData += data;
if(queryData.length > 1e6) {
queryData = "";
response.writeHead(413, {'Content-Type': 'text/plain'}).end();
request.connection.destroy();
}
});
request.on('end', function() {
request.post = querystring.parse(queryData);
callback();
});
} else {
response.writeHead(405, {'Content-Type': 'text/plain'});
response.end();
}
}
使用示例:
http.createServer(function(request, response) {
if(request.method == 'POST') {
processPost(request, response, function() {
console.log(request.post);
// Use request.post here
response.writeHead(200, "OK", {'Content-Type': 'text/plain'});
response.end();
});
} else {
response.writeHead(200, "OK", {'Content-Type': 'text/plain'});
response.end();
}
}).listen(8000);
response.post
而不是更合乎逻辑的request.post
。我已经更新了帖子。 - Mahn如果您将数据编码为JSON,然后发送到Node.js,它将更加清洁。
function (req, res) {
if (req.method == 'POST') {
var jsonString = '';
req.on('data', function (data) {
jsonString += data;
});
req.on('end', function () {
console.log(JSON.parse(jsonString));
});
}
}
qs.parse()
,不如使用JSON.parse()
将主体转换为可用的内容。例如:var post = JSON.parse(body);
,然后通过post.fieldname
访问数据。(故事的寓意是,如果你对所看到的内容感到困惑,请不要忘记使用typeof
!) - wmassinghamrequest.setEncoding
来使其正常工作,否则它可能无法正确处理非 ASCII 字符。 - Farid Nouri Neshat对于任何想知道如何在不安装Web框架的情况下完成此琐事的人,我设法将其组合在一起。它几乎没有生产就绪,但似乎可以工作。
function handler(req, res) {
var POST = {};
if (req.method == 'POST') {
req.on('data', function(data) {
data = data.toString();
data = data.split('&');
for (var i = 0; i < data.length; i++) {
var _data = data[i].split("=");
POST[_data[0]] = _data[1];
}
console.log(POST);
})
}
}
您可以使用Node.js的请求体解析中间件body-parser
。
首先加载body-parser
。
$ npm install body-parser --save
一些示例代码
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.use(function (req, res) {
var post_data = req.body;
console.log(post_data);
})
更多文档可以在这里找到。
参考: https://nodejs.org/en/docs/guides/anatomy-of-an-http-transaction/
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
// at this point, `body` has the entire request body stored in it as a string
});
如果您希望使用纯Node.js,则可以按照下面所示的方法提取POST数据:
// Dependencies
const StringDecoder = require('string_decoder').StringDecoder;
const http = require('http');
// Instantiate the HTTP server.
const httpServer = http.createServer((request, response) => {
// Get the payload, if any.
const decoder = new StringDecoder('utf-8');
let payload = '';
request.on('data', (data) => {
payload += decoder.write(data);
});
request.on('end', () => {
payload += decoder.end();
// Parse payload to object.
payload = JSON.parse(payload);
// Do smoething with the payload....
});
};
// Start the HTTP server.
const port = 3000;
httpServer.listen(port, () => {
console.log(`The server is listening on port ${port}`);
});
querystring
模块被认为是遗留的且未维护。 - Andrea Giammarchi