如何从Node.js中的HTTP post方法中提取表单数据(form[method="post"])和文件上传?

我看了文件,谷歌了一下,什么都没找到。

function (request, response) {
    //request.post????
}

有图书馆或黑客吗?


当前回答

这里的许多答案不再是好的实践,或者不能解释任何事情,所以这就是我写这篇文章的原因。

基础知识

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是很好的。

其他回答

如果你从POST接收到JSON格式的数据。:

  import http from 'http';
  const hostname  = '127.0.0.1'; 
  const port = 3000;

  const httpServer:  http.Server = http.createServer((req: http.IncomingMessage, res: 
         http.ServerResponse) => {

        if(req.method === 'POST') {
        let body: string = ''; 
          req.on('data',(chunck) => {
            body += chunck;
          });

          req.on('end', () => {
            const body = JSON.parse(body);
            res.statusCode = 200;
            res.end('OK post');
          });
       }
 
     });

     httpServer.listen(port, hostname, () => {
       console.info(`Server started at port ${port}`);
     })

这里的许多答案不再是好的实践,或者不能解释任何事情,所以这就是我写这篇文章的原因。

基础知识

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是很好的。

如果你不想把数据和数据回调放在一起,你可以像这样使用可读回调:

// Read Body when Available
request.on("readable", function(){
  request.body = '';
  while (null !== (request.body += request.read())){}
});

// Do something with it
request.on("end", function(){
  request.body //-> POST Parameters as String
});

这种方法修改传入的请求,但是一旦您完成响应,请求就会被垃圾收集,因此这应该不是问题。

如果你害怕巨大的身体,一种先进的方法是先检查身体的大小。

要详细说明使用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

如果希望表单数据在req.body中可用,则需要使用bodyParser()。 Body-parser解析您的请求并将其转换为可以轻松提取所需相关信息的格式。

例如,假设在前端有一个注册表单。您正在填写它,并请求服务器将详细信息保存在某个地方。

如果使用体解析器,则从请求中提取用户名和密码如下所示。

.............................................................

var loginDetails = {

username : request.body.username,

password : request.body.password

};