如何在node.js中处理POST请求

时间:2013-03-15 07:49:55

标签: javascript html5 node.js javascript-events

我正在尝试处理发送到node.js服务器的帖子请求。 名为server.js的JavaScript文件在浏览器上显示一个表单。我希望在将表单值发布到node.js后端后访问它们。

表单包含用户名,存储库和分支。提交表单时,我想将此数据显示回用户。

server.js代码:

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

1 个答案:

答案 0 :(得分:145)

我将使用您提供的代码,并提供比您的问题中涵盖的内容更为全面的答案,以便为“遥远的未来”中的人们提供帮助。我还将提供一个使用&#34; Vanilla JS&#34; (http://www.vanilla-js.com/)因为我认为太多的时髦人士说&#34;使用框架&#34;当你试图了解它是如何工作的时候。我认为他们这样做的原因是因为有人告诉他们使用框架&#34;当他们学习如何运作时。因为他们不是黑客,所以他们并不关心尝试和理解这个过程,所以他们中的许多人通常不了解如何在没有框架的情况下自己做到这一点(因此无处不在&#34 ;使用框架&#34;)。通过了解幕后的内容,您将成为更好的黑客,我希望这个答案可以帮助您。

既然您希望通过您正在输出的表单接受POST(表单)数据,那么在您的服务器中提供路由机制是必要的。这意味着您将告诉您的服务器将表单提供给访问您网站的人,但如果用户提交表单,Node会将POST数据路由到一个小的处理函数。我先提供了完整的答案,然后再进一步解剖,以方便那些想要从代码中学习的人。

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

现在解释为什么我做了我做过的事情。

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

首先,您要添加Node的内置&#39;查询字符串&#39;模块来解析实际的表单数据。

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) {

(我通常会将此功能的参数缩短为&#34; req&#34;以及&#34; res&#34;,但这只是我的偏好。)

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

这里我提供了一个简单的路由示例。在这种情况下,我们让我们的服务器监听&#34; favicon.ico&#34; - 与所有主要浏览器几乎所有初始网页请求一起提出的请求。此文件是您可以在您访问的每个网页的标签中看到的小图标。出于我们的目的,我们不需要提供图标,但我们会处理它的入站请求以显示一些基本的路由机制。

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

如果访问者使用默认的GET方法将浏览器指向服务器上的任何其他资源(除了&#34; favicon.ico&#34;我们刚刚在上面处理过),我们将为他们提供表单。

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

否则,如果您的访问者在您的服务器上指向POST,则他们很可能已经提交了他们使用之前的GET请求检索到的表单。

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

这里我们正在侦听名为&#34; / inbound&#34;的入站请求哪个 - 如果你发现上面的小细节 - 是&#34;行动&#34;我们的HTML表单。正如您所知,&#34;行动&#34;表单告诉浏览器将表单数据发送到何处。

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

这看起来有点令人困惑,但我保证不会。 POST请求可以作为来自客户端浏览器的多部分消息发送。如果表单中只有少量变量,那么您很可能不会看到这一点,但是当您扩展处理的数据量时,您会看到这一点。如果您需要观察,您还会看到if()语句询问POST数据的长度。恶意的人可以通过上传无尽的文件来杀死您的服务器,但如果我们采取行动则不会。这会将POST数据体限制为大约10兆字节,但您应该相应地进行调整。了解这些事情可以防止将来头痛,我也不希望你头疼。

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

这是我们使用表单数据的地方。由于Javascript的性质,这些变量名称是CASE SENSITIVE(例如&#34; UserName&#34;而不是&#34; username&#34;)。当然,您可以使用此数据执行任何操作(请记住Node的事件循环和异步特性)。

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

要继续我们的路由示例,我们在此处所做的工作包括if()语句下面的全部内容,该语句向客户端发送通用404&#34;未找到&#34;回复我们尚未处理的任何POST请求。

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

现在我们刚刚关闭了代码,包括一些用奇怪方法处理请求的代码。有一些我无法解决的问题(功能结构,空表格数据等),但确实有很多方法可以实现您的目标。正如我的CS教授多年前所说的那样,有很多方法可以编写一个程序,通过分享他们的作业很容易看出谁在作弊。

我希望你(以及其他任何人)能够看到使用内置模块在Node中做事情而不依赖于外部第三方库(例如外部第三方库)不是一些深奥甚至稍微困难的过程表达。这些图书馆在世界上占有一席之地,但不要跟随牧群:对你的代码作出明智的决定,因为在一天结束时,你是负责它的人(不是一些人) Stack Overflow)。