How to process POST data in Node.js?

Как вы извлекаете данные формы (form[method="post"]) и загрузки файлов, отправленные с HTTP POST метод в Node.js?

Я прочитал документацию, гуглил и ничего не нашел.

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

Есть библиотека или взломать?

31 ответ

Решение

Если вы используете Express (высокопроизводительная, высококлассная веб-разработка для Node.js), вы можете сделать это:

HTML:

<form method="post" action="/">
    <input type="text" name="user[name]">
    <input type="text" name="user[email]">
    <input type="submit" value="Submit">
</form>

JavaScript:

app.use(express.bodyParser());

app.post('/', function(request, response){
    console.log(request.body.user.name);
    console.log(request.body.user.email);
});

ОБНОВЛЕНО 1/ Июнь /2016:

Способ выше не рекомендуется использовать сейчас:

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)
});

Вы можете использовать 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.
        });
    }
}

Теперь, например, если у вас есть input поле с именем age Вы можете получить к нему доступ, используя переменную post:

console.log(post.age);

Многие ответы здесь больше не являются хорошей практикой или ничего не объясняют, поэтому я и пишу это.

Когда вызывается обратный вызов http.createServer, это когда сервер фактически получил все заголовки для запроса, но возможно, что данные еще не были получены, поэтому мы должны ждать его. Объект http-запроса (экземпляр http.IncomingMessage) на самом деле является читаемым потоком. В читаемых потоках всякий раз, когда прибывает кусок данных, 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)

Если вы пытаетесь принять отправку POST HTML-форм без файлов или обрабатывает вызовы jQuery ajax с типом контента по умолчанию, тогда тип контента application/x-www-form-urlencoded с uft-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)

Как уже отмечалось в других ответах, имейте в виду, что злонамеренные клиенты могут отправлять вам огромное количество данных, чтобы вызвать сбой вашего приложения или заполнить вашу память, чтобы защитить вас, чтобы убедиться, что вы отбрасываете запросы, которые передают данные, превышающие определенный предел. Если вы не используете библиотеку для обработки входящих данных. Я бы предложил использовать что-то вроде stream-meter, которое может прервать запрос, если достигнет указанного предела:

limitedStream = request.pipe(meter(1e7));
limitedStream.on('data', ...);
limitedStream.on('end', ...);

или же

request.pipe(meter(1e7)).pipe(createWriteStream(...));

или же

concat(request.pipe(meter(1e7)), ...);

Также попробуйте использовать модули npm, реализовав их самостоятельно, поскольку они, вероятно, будут лучше обрабатывать крайние случаи. Для экспресса я предлагаю использовать body-parser. Для Коа есть аналогичный модуль.

Если вы не используете рамки, тело довольно хорошо.

Обязательно разорвите соединение, если кто-то попытается заполнить вашу 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);

Будет лучше, если вы закодируете свои данные в 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));
        });
    }
}

Для тех, кто интересуется, как выполнить эту тривиальную задачу без установки веб-фреймворка, мне удалось собрать все вместе. Вряд ли производство готово, но, похоже, работает.

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);
        })
    }
}

Ты можешь использовать body-parserпромежуточное программное обеспечение для синтаксического анализа тела Node.js

Первая загрузка 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}`);
});

Вот как вы можете сделать это, если вы используете node-formidable:

var formidable = require("formidable");

var form = new formidable.IncomingForm();
form.parse(request, function (err, fields) {
    console.log(fields.parameter1);
    console.log(fields.parameter2);
    // ...
});
  1. устанавливать 'body-parser' с нпм.
  2. затем откройте app.ts -> написать -> var bodyParser = require('body-parser');
  3. тогда вам нужно написать app.use(bodyParser.json()) в app.ts модуль
  4. имейте в виду, что вы включаете app.use(bodyParser.json()) в верхней части или перед любым объявлением модуля. Пример: app.use(bodyParser.json()) app.use('/user',user);

  5. Тогда используйте var postdata = req.body;

Node.js 18 современным асинхронным способом с нулевыми зависимостями:

сервер.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.`);
})

Если вы не хотите разбивать свои данные на части вместе с data Обратный звонок вы всегда можете использовать readable обратный вызов, как это:

// 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
});

Этот подход изменяет входящий запрос, но как только вы закончите свой ответ, запрос будет собираться мусором, так что это не должно быть проблемой.

Продвинутым подходом было бы сначала проверить размер тела, если вы боитесь огромных тел.

Вам необходимо получить POST данные в блоках с использованием request.on('data', function(chunk) {...})

const http = require('http');

http.createServer((req, res) => {
    if (req.method == 'POST') {
        whole = ''
        req.on('data', (chunk) => {
            # consider adding size limit here
            whole += chunk.toString()
        })

        req.on('end', () => {
            console.log(whole)
            res.writeHead(200, 'OK', {'Content-Type': 'text/html'})
            res.end('Data received.')
        })
    }
}).listen(8080)

Вы должны рассмотреть возможность добавления ограничения размера в указанной позиции, как предложено.

Есть несколько способов сделать это. Однако самый быстрый способ, который я знаю, - это использовать библиотеку Express.js с body-parser.

var express = require("express");
var bodyParser = require("body-parser");
var app = express();

app.use(bodyParser.urlencoded({extended : true}));

app.post("/pathpostdataissentto", function(request, response) {
  console.log(request.body);
  //Or
  console.log(request.body.fieldName);
});

app.listen(8080);

Это может работать для строк, но я бы изменил bodyParser.urlencoded на bodyParser.json, если данные POST содержат массив JSON.

Больше информации: http://www.kompulsa.com/how-to-accept-and-parse-post-requests-in-node-js/

Экспресс v4.17.0

app.use(express.urlencoded( {extended: true} ))

Если вы используете Express.js, прежде чем вы сможете получить доступ к req.body, вы должны добавить middleware bodyParser:

app.use(express.bodyParser());

Тогда вы можете попросить

req.body.user

если вы получаете данные из 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}`);
     })

Для тех, кто использует сырую двоичную загрузку POST без накладных расходов на кодирование, вы можете использовать:

клиент:

var xhr = new XMLHttpRequest();
xhr.open("POST", "/api/upload", true);
var blob = new Uint8Array([65,72,79,74]); // or e.g. recorder.getBlob()
xhr.send(blob);

сервер:

var express = require('express');
var router = express.Router();
var fs = require('fs');

router.use (function(req, res, next) {
  var data='';
  req.setEncoding('binary');
  req.on('data', function(chunk) {
    data += chunk;
  });

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

router.post('/api/upload', function(req, res, next) {
  fs.writeFile("binaryFile.png", req.body, 'binary', function(err) {
    res.send("Binary POST successful!");
  });
});

И если вы не хотите использовать весь фреймворк, такой как Express, но вам также нужны различные виды форм, в том числе загрузки, тогда формалайн может быть хорошим выбором.

Он указан в модулях Node.js

Вы можете извлечь параметр сообщения без использования экспресс.

1: nmp install multiparty

2: импорт многопартийности. как var multiparty = require('multiparty');

3: `

if(req.method ==='POST'){
   var form = new multiparty.Form();
   form.parse(req, function(err, fields, files) {
      console.log(fields['userfile1'][0]);
    });
    }

4: и ФОРМА HTML ЕСТЬ.

<form method=POST enctype=multipart/form-data>
<input type=text name=userfile1><br>
<input type=submit>
</form>

Я надеюсь, что это будет работать для вас. Благодарю.

На полях формы, как эти

   <input type="text" name="user[name]" value="MyName">
   <input type="text" name="user[email]" value="myemail@somewherefarfar.com">

некоторые из приведенных выше ответов потерпят неудачу, потому что они поддерживают только плоские данные.

Сейчас я использую ответ Кейси Чу, но с модулем "qs" вместо модуля "querystring". Это модуль, который использует body-parser. Поэтому, если вы хотите вложенные данные, вы должны установить qs.

npm install qs --save

Затем замените первую строку следующим образом:

//var qs = require('querystring');
var qs = require('qs'); 

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);
            console.log(post.user.name); // should work
            // use post['blah'], etc.
        });
    }
}

Я нашел видео, которое объясняет, как этого добиться: 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);

Вы можете использовать промежуточное программное обеспечение Express, в которое теперь встроен анализатор тела. Это означает, что все, что вам нужно сделать, это следующее:

import express from 'express'

const app = express()

app.use(express.json())

app.post('/thing', (req, res) => {
  console.log(req.body) // <-- this will access the body of the post
  res.sendStatus(200)
})

Этот пример кода - ES6 с Express 4.16.x

Вам нужно использовать bodyParser(), если вы хотите, чтобы данные формы были доступны в req.body. body-parser анализирует ваш запрос и преобразует его в формат, из которого вы можете легко извлечь необходимую информацию, которая может вам понадобиться.

Например, предположим, что у вас есть форма регистрации на вашем интерфейсе. Вы заполняете его и запрашиваете сервер где-нибудь сохранить данные.

Извлечение имени пользователя и пароля из вашего запроса происходит так же просто, как показано ниже, если вы используете body-parser.

…………………………………………………….

var loginDetails = {

username : request.body.username,

password : request.body.password

};

Если это связано с загрузкой файла, браузер обычно отправляет его как "multipart/form-data" Тип содержимого. Вы можете использовать это в таких случаях

var multipart = require('multipart');
multipart.parse(req)

Ссылка 1

Ссылка 2

Чтобы уточнить использование 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

Ограничьте размер POST, чтобы избежать затопления вашего узла приложения. Существует отличный модуль raw-body, подходящий как для экспресс, так и для соединения, который может помочь вам ограничить запрос по размеру и длине.

Вы можете легко отправить и получить ответ на запрос POST, используя "Запрос - упрощенный HTTP-клиент" и Javascript Promise.

var request = require('request');

function getData() {
    var options = {
        url: 'https://example.com',
        headers: {
            'Content-Type': 'application/json'
        }
    };

    return new Promise(function (resolve, reject) {
        var responseData;
        var req = request.post(options, (err, res, body) => {
            if (err) {
                console.log(err);
                reject(err);
            } else {
                console.log("Responce Data", JSON.parse(body));
                responseData = body;
                resolve(responseData);
            }
        });
    });
}
Другие вопросы по тегам