47

I'm trying to handle a post request being sent to my node.js server. JavaScript file with a name server.js displays a form on the browser. I want access to the form values after they are posted to the node.js backend.

The form contains a username, repository, and branch. When the form is submitted I want to display back this data back to the user.

The server.js code:

var http = require('http');

http.createServer(function (request, response) {
response.writeHead(200, {'Content-Type': 'text/html'});
response.end('<html><body>'
    + '<h1>XYZ Repository Commit Monitor</h1>'
    + '<form method="post" action="." enctype="application/x-www-form-urlencoded"><fieldset>'
    + '<div><label for="UserName">User Name:</label><input type="text" id="UserName" name="UserName" /></div>'
    + '<div><label for="Repository">Repository:</label><input type="text" id="Repository" name="Repository" /></div>'
    + '<div><label for="Branch">Branch:</label><input type="text" id="Branch" name="Branch" value="master" /></div>'
    + '<div><input id="ListCommits" type="submit" value="List Commits" /></div>'
    + '</fieldset></form>'
    + '</body></html>');
}).listen(8124);

console.log('Server running at http://127.0.0.1:8124/');
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Sau
  • 2,109
  • 6
  • 21
  • 22
  • 14
    I would strongly recommend you use a (even low level) framework for building apps with Node. I personally use Express (http://expressjs.com/), however there are other options if you choose. Amongst other things it will allow you to easily handle different request types and routes, as well as static content. – Nick Mitchinson Mar 15 '13 at 08:06
  • See http://stackoverflow.com/questions/6158933/http-post-request-in-node-js – user568109 Mar 15 '13 at 11:00
  • You can see a simple example on how to handle HTTP POST with Express.js in my blog http://hectorcorrea.com/blog/introduction-to-node-js – Hector Correa Mar 15 '13 at 13:23
  • use expressjs. you will get sample server.js also. http://expressjs.com/guide.html . then use app.post("/pageurl", function(req, res){ /* use req.body.UserName, req.body.Branch etc */ }); – gp. Aug 30 '13 at 15:50
  • 3
    possible duplicate of [How do you extract POST data in node.js?](http://stackoverflow.com/questions/4295782/how-do-you-extract-post-data-in-node-js) – moka Sep 10 '13 at 10:05

1 Answers1

158

I'm going to use the code you provided, and provide an answer more thorough than what's covered in your question to accommodate people in The Distant Future. I will also provide an answer that uses "Vanilla JS" (http://www.vanilla-js.com/) because I think too many hipsters say "use a framework" when you're trying to learn how this works. I think the reason they do that is because someone told them to "use a framework" when they were learning how this works. Because they aren't hackers, they didn't care to try and understand the process, so very often many of them do not understand how to do this on their own, without a framework (hence the ubiquitous "use a framework"). You will become a better hacker by understanding what's going on under the hood and I hope this answer helps you in that regard.

Now that you're wanting to accept POST (form) data via the form you're outputting, it's necessary to provide a routing mechanism in your server. This means that you'll tell your server to give the form to people visiting your site, but then if the user submits a form, Node will route the POST data to a little processing function. I've provided the complete answer first and then dissected it further down, to accommodate people wanting to learn from the code.

var http = require('http');
var qs = require('querystring');
var formOutput = '<html><body>'
  + '<h1>XYZ Repository Commit Monitor</h1>'
  + '<form method="post" action="inbound" enctype="application/x-www-form-urlencoded"><fieldset>'
  + '<div><label for="UserName">User Name:</label><input type="text" id="UserName" name="UserName" /></div>'
  + '<div><label for="Repository">Repository:</label><input type="text" id="Repository" name="Repository" /></div>'
  + '<div><label for="Branch">Branch:</label><input type="text" id="Branch" name="Branch" value="master" /></div>'
  + '<div><input id="ListCommits" type="submit" value="List Commits" /></div></fieldset></form></body></html>';
var serverPort = 8124;
http.createServer(function (request, response) {
  if(request.method === "GET") {
    if (request.url === "/favicon.ico") {
      response.writeHead(404, {'Content-Type': 'text/html'});
      response.write('<!doctype html><html><head><title>404</title></head><body>404: Resource Not Found</body></html>');
      response.end();
    } else {
      response.writeHead(200, {'Content-Type': 'text/html'});
      response.end(formOutput);
    }
  } else if(request.method === "POST") {
    if (request.url === "/inbound") {
      var requestBody = '';
      request.on('data', function(data) {
        requestBody += data;
        if(requestBody.length > 1e7) {
          response.writeHead(413, 'Request Entity Too Large', {'Content-Type': 'text/html'});
          response.end('<!doctype html><html><head><title>413</title></head><body>413: Request Entity Too Large</body></html>');
        }
      });
      request.on('end', function() {
        var formData = qs.parse(requestBody);
        response.writeHead(200, {'Content-Type': 'text/html'});
        response.write('<!doctype html><html><head><title>response</title></head><body>');
        response.write('Thanks for the data!<br />User Name: '+formData.UserName);
        response.write('<br />Repository Name: '+formData.Repository);
        response.write('<br />Branch: '+formData.Branch);
        response.end('</body></html>');
      });
    } else {
      response.writeHead(404, 'Resource Not Found', {'Content-Type': 'text/html'});
      response.end('<!doctype html><html><head><title>404</title></head><body>404: Resource Not Found</body></html>');
    }
  } else {
    response.writeHead(405, 'Method Not Supported', {'Content-Type': 'text/html'});
    return response.end('<!doctype html><html><head><title>405</title></head><body>405: Method Not Supported</body></html>');
  }
}).listen(serverPort);
console.log('Server running at localhost:'+serverPort);

And now for the breakdown explaining why I have done the things I did.

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

First, you're going to add Node's built-in 'querystring' module to parse the actual form data.

var formOutput = '<html><body>'
  + '<h1>XYZ Repository Commit Monitor</h1>'
  + '<form method="post" action="/inbound" enctype="application/x-www-form-urlencoded"><fieldset>'
  + '<div><label for="UserName">User Name:</label><input type="text" id="UserName" name="UserName" /></div>'
  + '<div><label for="Repository">Repository:</label><input type="text" id="Repository" name="Repository" /></div>'
  + '<div><label for="Branch">Branch:</label><input type="text" id="Branch" name="Branch" value="master" /></div>'
  + '<div><input id="ListCommits" type="submit" value="List Commits" /></div></fieldset></form></body></html>';
var serverPort = 8124;

I've moved the form output up above our server/routing/form-handling mechanism, because the logic is then much easier to read. I also moved the server listening port information up here, because you then only have to change it in one place instead of many below.

http.createServer(function (request, response) {

(I usually shorten the parameters of this function to "req" and "res", but that's just my preference.)

  if(request.method === "GET") {
    if (request.url === "/favicon.ico") {
      response.writeHead(404, {'Content-Type': 'text/html'});
      response.write(notFound);
      response.end();

Here I've included a simple routing example. In this case, we're having our server listen for requests for "favicon.ico" -- a request made alongside almost all initial requests for a webpage by all the major browsers. This file is the little icon you can see up in the tabs of each webpage you're visiting. For our purposes, we don't need to serve a favicon, but we will handle inbound requests for it to show some basic routing mechanics.

    } else {
      response.writeHead(200, {'Content-Type': 'text/html'});
      response.end(formOutput);
    }

If your visitors point their browser to ANY other resource on your server with the default GET method (besides the "favicon.ico" we just handled above), we will serve them the form.

  } else if(request.method === "POST") {

Otherwise, if your visitors are pointing a POST at your server, it's very likely they have submitted the form they retrieved with the previous GET request.

    if (request.url === "/inbound") {

Here we are listening for inbound requests called "/inbound" which -- if you caught the little detail above -- is the "action" of our HTML form. As you may know, the "action" of the form tells the browser where to send the form data.

      var requestBody = '';
      request.on('data', function(data) {
        requestBody += data;
        if(requestBody.length > 1e7) {
          response.writeHead(413, 'Request Entity Too Large', {'Content-Type': 'text/html'});
          response.end('<!doctype html><html><head><title>413</title></head><body>413: Request Entity Too Large</body></html>');
        }
      });
      request.on('end', function() {
        var formData = qs.parse(requestBody);

This might look a little confusing, but I promise it's not. POST requests can be sent as multi-part messages from the client browser. With something as small as a few variables in a form, you won't likely ever see this, but as you scale the amount of data you handle, you will see this. If you're observant, you'll also see the if() statement asking about the length of the POST data. A malicious person can kill your server by uploading an endless file, but not if we take action. This limits the POST data body to about ten megabytes, but you should adjust accordingly. Knowing about these things prevents a future headache, and I don't want you to have a headache.

        response.writeHead(200, {'Content-Type': 'text/html'});
        response.write('<!doctype html><html><head><title>response</title></head><body>');
        response.write('Thanks for the data!<br />User Name: '+formData.UserName);
        response.write('<br />Repository Name: '+formData.Repository);
        response.write('<br />Branch: '+formData.Branch);
        response.end('</body></html>');
      });

And here is where we use the form data. Because of the nature of Javascript, these variable names are CASE SENSITIVE (such as "UserName" instead of "username"). Of course, you can do anything you want with this data (keeping in mind Node's event loop and asynchronous nature).

    }
    response.writeHead(404, 'Resource Not Found', {'Content-Type': 'text/html'});
    return response.end('<!doctype html><html><head><title>404</title></head><body>413: Request Entity Too Large</body></html>');

To continue our routing example, what we've done here is included a catch-all underneath the if() statement which sends the client a generic 404 "Not Found" reply to any POST request we haven't already handled.

  } else {
    response.writeHead(405, 'Method Not Supported', {'Content-Type': 'text/html'});
    return response.end('<!doctype html><html><head><title>405</title></head><body>405: Method Not Supported</body></html>');
  }
}).listen(serverPort);
console.log('Server running at localhost:'+serverPort);

And now we've just finished the code off, including a little bit of code to handle requests with strange methods. There are a few things I haven't addressed (function structure, empty form data, etc.), but there are indeed many ways to accomplish your goals. As one of my CS professors once said many years ago, there are so many ways to program a program that it's easy to see who's cheating by sharing their homework.

I hope that you (and anyone else) can see that it's not some esoteric or even slightly difficult process to do things in Node using its built-in modules instead of relying on external third-party libraries such as Express. Those libraries have their place in the world, but don't follow the herd: Make an informed decision about your code, because at the end of the day, you're the one responsible for it (not some people on Stack Overflow).

L0j1k
  • 12,255
  • 7
  • 53
  • 65
  • 3
    It's the thought that counts. Thanks! :) – L0j1k Mar 10 '14 at 21:57
  • 1
    What about multipart form data? – Quentin Engles May 27 '14 at 10:13
  • 1
    Sorry to bother you with a year-old answer, but could you expand it a little bit to cover how to handle routes? Great answer, though - well-written and to the point. – Nekkoru Nov 05 '14 at 10:50
  • 1
    Sure! I've added a very basic routing example to the answer. Also, multipart form data is a bit more complex, so I think an example of that would best be given in another answer. – L0j1k Nov 05 '14 at 16:34
  • 4
    I love this answer. People who say "use Express, it saves you the trouble of knowing anything", really annoy me. It makes me feel like I'm learning how to play around with a robot instead of building the robot. – DeepS1X May 10 '17 at 20:51
  • 1
    I really love your answer, as I've rolled my own no external package, MVC framework with many features inspired by RoR and MVC.net. Do you have anything on sending email from POSTs, without using external libraries? I'm having great difficulty finding something "Vanilla". – iuppiter Aug 03 '17 at 09:29
  • @iuppiter If you are looking to send email to various email addresses from a variety of email providers, for example on a marketing list or newsletter, you're going to need an external SMTP server to send mail, there's no way around that. It is possible to write your own SMTP server using Node, but this is waaay more complex than what's been done here. I'd be willing to write up either case as an answer to another question, but it's definitely out of the scope of this question for me to update it. – L0j1k Apr 02 '19 at 14:04