官术网_书友最值得收藏!

Processing POST data

If we want to be able to receive POST data, we have to instruct our server how to accept and handle a POST request. In PHP, we could access our POST values seamlessly with $_POST['fieldname'], because it would block until an array value was filled. Contrariwise, Node provides low-level interaction with the flow of HTTP data, allowing us to interface with the incoming message body as a stream, leaving it entirely up to the developer to turn that stream into usable data

Getting ready

Let's create a server.js file ready for our code, and an HTML file called form.html, that contains the following:

<form method=post>
  <input type=text name=userinput1><br>
  <input type=text name=userinput2><br>
  <input type=submit>
</form>

For our purposes, we'll place form.html in the same folder as server.js. Though this is not generally a recommended practice, we should usually place our public code in a separate folder from our server code.

How to do it...

We'll provision our server for both GET and POST requests. Let's start with GET by requiring the http module and loading form.html to serve through createServer, as follows:

var http = require('http');
var form = require('fs').readFileSync('form.html');
http.createServer(function (request, response) {
  if (request.method === "GET") {
    response.writeHead(200, {'Content-Type': 'text/html'});
    response.end(form);
  }
}).listen(8080);

We are synchronously loading form.html at initialization time instead of accessing the disk on each request. If we navigate to localhost:8080, we'll be presented with a form. But if we fill out our form, nothing happens because we need to handle POST requests, as follows:

if (request.method === "POST") {
  var postData = '';
  request.on('data', function (chunk) {
    postData += chunk;
  }).on('end', function() {
    console.log('User Posted:\n' + postData);
    response.end('You Posted:\n' + postData);
  });
}

Once the form is completed and submitted, the browser and console will output the raw query string sent from the client. Converting our postData variable into an object provides an easy way to interact with and manipulate the submitted information. The querystring module has a parse method, which transforms query strings into objects, and since form submission arrives in query string format, we can use it to objectify our data as follows:

var http = require('http');
var querystring = require('querystring');
var util = require('util');
var form = require('fs').readFileSync('form.html');

http.createServer(function (request, response) {
  if (request.method === "POST") {
    var postData = '';
    request.on('data', function (chunk) {
      postData += chunk;
    }).on('end', function () {
 var postDataObject = querystring.parse(postData);
      console.log('User Posted:\n', postData);
 response.end('You Posted:\n' + util.inspect(postDataObject));
    });
  }
  if (request.method === "GET") {
    response.writeHead(200, {'Content-Type': 'text/html'});
    response.end(form);
  }
}).listen(8080);

Notice the util module—we require it to use its inspect method for a simple way to output our postDataObject to the browser.

Finally, we're going to protect our server from memory overload exploits.

Note

Protecting a POST server

V8 (and therefore Node) has virtual memory limitations based upon the processor architecture and operating system constraints. If we don't restrict the amount of data our POST server accepts, we could leave ourselves open for a type of denial-of-service attack. Without protection, an extremely large POST request could cause our server to slow down significantly or even crash.

To achieve this, we'll set a variable for the maximum acceptable data size and check it against the growing length of our postData variable as follows:

var http = require('http');
var querystring = require('querystring');
var util = require('util');
var form = require('fs').readFileSync('form.html');
var maxData = 2 * 1024 * 1024; //2mb
http.createServer(function (request, response) {
  if (request.method === "POST") {
    var postData = '';
    request.on('data', function (chunk) {
      postData += chunk;
 if (postData.length > maxData) {
 postData = '';
 this.destroy();
 response.writeHead(413); // Request Entity Too Large
 response.end('Too large');
 }
    }).on('end', function () {
 if (!postData) { response.end(); return; } // prevents empty post
 // requests from
 // crashing the server
 var postDataObject = querystring.parse(postData);
 console.log('User Posted:\n', postData);
 response.end('You Posted:\n' + util.inspect(postDataObject));
 });
//rest of our code....

How it works...

Once we know that a POST request has been made for our server (by checking request.method), we aggregate our incoming data into our postData variable via the data event listener on the request object. However, if we find that the submitted data exceeds our maxData limit, we clear our postData variable and pause the incoming stream, preventing any further data arriving from the client (using stream.destroy instead of stream.pause seems to interfere with our response mechanism. Once a stream has been paused for a while, it is automatically removed from memory by V8's garbage collector).

We then send a 413 Request Entity Too Large HTTP header. In the end event listener, as long as postData hasn't been cleared for exceeding maxData (or wasn't blank in the first place), we use querystring.parse to turn our POST message body into an object. From this point on, we can perform any number of interesting activities, such as manipulate, analyze, and pass it to a database. However, for the example, we simply output postDataObject to the browser and postData to the console.

In Chapter 5, Employing Streams, we'll look into ways to constrain the actual stream input to a maximum size and abort if that size is exceeded by using the post-Node 0.8 extra stream API's.

There's more...

If we want our code to look a little more elegant and we're not so concerned about handling POST data as a stream, we can employ a user land (non-core) module to get a little sugar on our syntax.

Accessing POST data with connect and body-parser

Connect is an excellent middleware framework for Node, providing a method framework that assimilates a higher level of abstraction for common server tasks. The body-parser module is actually part of the Express web framework (which was inspired by, and originally built on top of Connect). We will be discussing Express in detail in Chapter 7, Accelerating Development with Express.

By chaining body-parser to a normal callback function, we suddenly have access to the POST data via request.body (when data is sent by the POST request, it is held in the message body). The request.body object turns out to be exactly the same object as the postDataObject we generated in our recipe.

First, let's make sure we have Connect and body-parser installed by executing the following commands:

npm install connect
npm install body-parser

We can require connect in place of http, since it provides us with createServer capabilities. To access the createServer method, we can use connect.createServer, or the shorthand version, which is simply connect. Connect allows us to combine multiple pieces of middleware together, by passing them as parameters to the createServer method. The following code shows how to implement similar behavior to what is as seen in the recipe, using Connect:

var connect = require('connect');
var bodyParser = require('body-parser');
var util = require('util');
var form = require('fs').readFileSync('form.html');
connect(connect.limit('2mb'), bodyParser(),function (request, response) {
  if (request.method === "POST") {
    console.log('User Posted:\n', request.body);
    response.end('You Posted:\n' + util.inspect(request.body));
  }
  if (request.method === "GET") {
    response.writeHead(200, {'Content-Type': 'text/html'});
    response.end(form);
  }
}).listen(8080);

Notice that we are no longer using the http module directly. We pass connect.limit as our first parameter to achieve the same maxData restriction implemented in the main example.

Next, we pass in the bodyParser method, allowing connect to retrieve our POST data for us, objectifying the data into request.body. Finally, there's our callback function, with all the former POST functionality stripped out except the code to echo our data object (which is now request.body) to the console and browser. This is where we deviate slightly from our original recipe.

In the recipe, we return the raw postData variable to the console, but, here we return the request.body object. To output raw data with Connect would either take pointless deconstruction of our object to reassemble the raw query string or an extension of the bodyParser function. This is the trade off with using third-party modules; we can only easily interact with information the module author expects us to interact with.

Let's look under the hood for a moment. If we fire up an instance of node without any arguments, we can access Read-Eval-Print-Loop (REPL), which is the Node command-line environment. In REPL, we can write the following:

console.log(require('body-parser').toString());

If we look at the output, we'll see its connect.bodyParser function code and should be able to easily identify the similarities between the connect.bodyParser code and our main recipe's code.

See also

  • The Handling file uploads recipe
  • The Browser-server transmission via AJAX recipe discussed in Chapter 3, Working with Data Serialization
  • The Initializing and using a session recipe discussed in Chapter 7, Accelerating Development with Express
主站蜘蛛池模板: 景德镇市| 南江县| 西贡区| 郸城县| 桓仁| 吉安市| 自贡市| 永年县| 绥化市| 桐庐县| 赤峰市| 蚌埠市| 慈溪市| 龙海市| 广宁县| 遵义县| 琼结县| 清镇市| 阳新县| 建湖县| 饶河县| 清苑县| 噶尔县| 安陆市| 凌海市| 嘉祥县| 长子县| 武胜县| 芜湖县| 南江县| 沁水县| 大冶市| 富蕴县| 磐石市| 昭觉县| 中超| 渝中区| 盘山县| 浮梁县| 灌阳县| 小金县|