如何从Node.js中的HTTP post方法中提取表单数据(form[method="post"])和文件上传?
我看了文件,谷歌了一下,什么都没找到。
function (request, response) {
//request.post????
}
有图书馆或黑客吗?
如何从Node.js中的HTTP post方法中提取表单数据(form[method="post"])和文件上传?
我看了文件,谷歌了一下,什么都没找到。
function (request, response) {
//request.post????
}
有图书馆或黑客吗?
当前回答
如果它涉及文件上传,浏览器通常以“multipart/form-data”内容类型发送它。 您可以在这种情况下使用它
var multipart = require('multipart');
multipart.parse(req)
参考1
参考2
其他回答
如果你使用Express (Node.js的高性能、高级web开发),你可以这样做:
HTML:
<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)
});
这里的许多答案不再是好的实践,或者不能解释任何事情,所以这就是我写这篇文章的原因。
基础知识
When the callback of http.createServer is called, is when the server has actually received all the headers for the request, but it's possible that the data has not been received yet, so we have to wait for it. The http request object(a http.IncomingMessage instance) is actually a readable stream. In readable streams whenever a chunk of data arrives, a data event is emitted(assuming you have registered a callback to it) and when all chunks have arrived an end event is emitted. Here's an example on how you listen to the events:
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)
这里的缓冲区。使用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)
如果您的内容类型是JSON,则可以简单地使用JSON。Parse而不是qs.parse。
如果你正在处理文件或处理多部分内容类型,那么在这种情况下,你应该使用像可怕的东西,它消除了处理它的所有痛苦。看看我的另一个答案,我在那里发布了多部分内容的有用链接和模块。
管道
如果你不想解析内容,而是将其传递到其他地方,例如将其作为数据发送到另一个http请求或将其保存到一个文件中,我建议管道而不是缓冲它,因为它将占用更少的代码,更好地处理回压,它将占用更少的内存,在某些情况下更快。
所以如果你想保存内容到一个文件:
http.createServer((request, response) => {
request.pipe(fs.createWriteStream('./request'));
}).listen(8080)
限制数据量
正如其他答案所指出的那样,我记得恶意客户端可能会发送大量数据来崩溃你的应用程序或填满你的内存,所以为了保护这一点,确保你放弃发出超过一定限制的数据的请求。如果不使用库来处理传入的数据。我建议使用类似流计的东西,它可以在达到指定的限制时中止请求:
limitedStream = request.pipe(meter(1e7));
limitedStream.on('data', ...);
limitedStream.on('end', ...);
or
request.pipe(meter(1e7)).pipe(createWriteStream(...));
or
concat(request.pipe(meter(1e7)), ...);
NPM模块
虽然我在上面描述了如何使用HTTP请求体 缓冲和解析内容,我建议使用这些模块之一,而不是自己实现,因为它们可能会更好地处理边缘情况。对于表达,我建议使用体解析器。对于koa,有一个类似的模块。
如果你不使用框架,body是很好的。
Node.js 18的现代异步方式,零依赖:
server.mjs:
import { createServer } from 'node:http';
const rawReqToString = async (req) => {
const buffers = [];
for await(const chunk of req){
buffers.push(chunk);
}
return Buffer.concat(buffers).toString();
};
const server = createServer(async (req, res) => {
const object = JSON.parse(await rawReqToString(req));
...
});
server.listen(3000, 'localhost', () => {
console.log(`The server is running.`);
})
要详细说明使用URLSearchParams:
Node.js知识:如何读取POST数据? 类:URLSearchParams .js文档 MDN: URLSearchParams
const http = require('http');
const POST_HTML =
'<html><head><title>Post Example</title></head>' +
'<body>' +
'<form method="post">' +
'Input 1: <input name="input1"><br>' +
'Input 2: <input name="input2"><br>' +
'Input 1: <input name="input1"><br>' +
'<input type="submit">' +
'</form>' +
'</body></html>';
const FORM_DATA = 'application/x-www-form-urlencoded';
function processFormData(body) {
const params = new URLSearchParams(body);
for ([name, value] of params.entries()) console.log(`${name}: ${value}`);
}
// req: http.IncomingMessage
// res: http.ServerResponse
//
function requestListener(req, res) {
const contentType = req.headers['content-type'];
let body = '';
const append = (chunk) => {
body += chunk;
};
const complete = () => {
if (contentType === FORM_DATA) processFormData(body);
res.writeHead(200);
res.end(POST_HTML);
};
req.on('data', append);
req.on('end', complete);
}
http.createServer(requestListener).listen(8080);
$ node index.js
input1: one
input2: two
input1: three
我找到了一个视频,它解释了如何实现这一点: https://www.youtube.com/watch?v=nuw48-u3Yrg
它使用默认的“http”模块以及“querystring”和“stringbuilder”模块。应用程序从网页中获取两个数字(使用两个文本框),并在提交时返回这两个数字的和(以及在文本框中持久化的值)。这是我能在其他地方找到的最好的例子。
相关源代码:
var http = require("http");
var qs = require("querystring");
var StringBuilder = require("stringbuilder");
var port = 9000;
function getCalcHtml(req, resp, data) {
var sb = new StringBuilder({ newline: "\r\n" });
sb.appendLine("<html>");
sb.appendLine(" <body>");
sb.appendLine(" <form method='post'>");
sb.appendLine(" <table>");
sb.appendLine(" <tr>");
sb.appendLine(" <td>Enter First No: </td>");
if (data && data.txtFirstNo) {
sb.appendLine(" <td><input type='text' id='txtFirstNo' name='txtFirstNo' value='{0}'/></td>", data.txtFirstNo);
}
else {
sb.appendLine(" <td><input type='text' id='txtFirstNo' name='txtFirstNo' /></td>");
}
sb.appendLine(" </tr>");
sb.appendLine(" <tr>");
sb.appendLine(" <td>Enter Second No: </td>");
if (data && data.txtSecondNo) {
sb.appendLine(" <td><input type='text' id='txtSecondNo' name='txtSecondNo' value='{0}'/></td>", data.txtSecondNo);
}
else {
sb.appendLine(" <td><input type='text' id='txtSecondNo' name='txtSecondNo' /></td>");
}
sb.appendLine(" </tr>");
sb.appendLine(" <tr>");
sb.appendLine(" <td><input type='submit' value='Calculate' /></td>");
sb.appendLine(" </tr>");
if (data && data.txtFirstNo && data.txtSecondNo) {
var sum = parseInt(data.txtFirstNo) + parseInt(data.txtSecondNo);
sb.appendLine(" <tr>");
sb.appendLine(" <td>Sum: {0}</td>", sum);
sb.appendLine(" </tr>");
}
sb.appendLine(" </table>");
sb.appendLine(" </form>")
sb.appendLine(" </body>");
sb.appendLine("</html>");
sb.build(function (err, result) {
resp.write(result);
resp.end();
});
}
function getCalcForm(req, resp, data) {
resp.writeHead(200, { "Content-Type": "text/html" });
getCalcHtml(req, resp, data);
}
function getHome(req, resp) {
resp.writeHead(200, { "Content-Type": "text/html" });
resp.write("<html><html><head><title>Home</title></head><body>Want to some calculation? Click <a href='/calc'>here</a></body></html>");
resp.end();
}
function get404(req, resp) {
resp.writeHead(404, "Resource Not Found", { "Content-Type": "text/html" });
resp.write("<html><html><head><title>404</title></head><body>404: Resource not found. Go to <a href='/'>Home</a></body></html>");
resp.end();
}
function get405(req, resp) {
resp.writeHead(405, "Method not supported", { "Content-Type": "text/html" });
resp.write("<html><html><head><title>405</title></head><body>405: Method not supported</body></html>");
resp.end();
}
http.createServer(function (req, resp) {
switch (req.method) {
case "GET":
if (req.url === "/") {
getHome(req, resp);
}
else if (req.url === "/calc") {
getCalcForm(req, resp);
}
else {
get404(req, resp);
}
break;
case "POST":
if (req.url === "/calc") {
var reqBody = '';
req.on('data', function (data) {
reqBody += data;
if (reqBody.length > 1e7) { //10MB
resp.writeHead(413, 'Request Entity Too Large', { 'Content-Type': 'text/html' });
resp.end('<!doctype html><html><head><title>413</title></head><body>413: Request Entity Too Large</body></html>');
}
});
req.on('end', function () {
var formData = qs.parse(reqBody);
getCalcForm(req, resp, formData);
});
}
else {
get404(req, resp);
}
break;
default:
get405(req, resp);
break;
}
}).listen(port);