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

Posted

技术标签:

【中文标题】如何在 node.js 中处理 POST 请求【英文标题】:How to handle POST request in node.js 【发布时间】:2013-03-03 20:40:30 【问题描述】:

我正在尝试处理发送到我的 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/');

【问题讨论】:

我强烈建议您使用(甚至是低级)框架来使用 Node.js 构建应用程序。我个人使用 Express (expressjs.com),但如果您选择,还有其他选项。除此之外,它还可以让您轻松处理不同的请求类型和路由,以及静态内容。 见***.com/questions/6158933/… 您可以在我的博客hectorcorrea.com/blog/introduction-to-node-js中看到一个关于如何使用 Express.js 处理 HTTP POST 的简单示例 使用 expressjs。您还将获得示例 server.js。 expressjs.com/guide.html 。然后使用 app.post("/pageurl", function(req, res) /* 使用 req.body.UserName, req.body.Branch 等 */ ); How do you extract POST data in node.js?的可能重复 【参考方案1】:

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

既然您希望通过您正在输出的表单接受 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 的内置“查询字符串”模块来解析实际的表单数据。

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) 

(我通常将此函数的参数缩短为“req”和“res”,但这只是我的偏好。)

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

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

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

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

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

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

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

在这里,我们正在侦听称为“/inbound”的入站请求,如果您发现了上面的小细节,它就是我们 HTML 表单的“动作”。您可能知道,表单的“动作”告诉浏览器将表单数据发送到哪里。

      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 的性质,这些变量名区分大小写(例如“用户名”而不是“用户名”)。当然,你可以对这些数据做任何你想做的事情(记住 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“未找到”回复给客户端我们尚未处理的任何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 的内置模块而不是依赖外部第三方库(例如 Express)在 Node 中做事并不是什么深奥甚至有点困难的过程。这些库在世界上占有一席之地,但不要随波逐流:对您的代码做出明智的决定,因为归根结底,您是负责它的人(而不是 Stack Overflow 上的某些人)。

【讨论】:

重要的是思想。谢谢! :) 多部分表单数据呢? 很抱歉用一个多年前的答案打扰您,但是您能否将其扩展一点以涵盖如何处理路线?不过,答案很好——写得很好,切中要害。 当然!我在答案中添加了一个非常基本的路由示例。此外,多部分表单数据有点复杂,所以我认为最好在另一个答案中给出一个例子。 我喜欢这个答案。那些说“用快递,省得你什么都知道的麻烦”的人,真的很烦我。这让我觉得我正在学习如何玩机器人而不是建造机器人。

以上是关于如何在 node.js 中处理 POST 请求的主要内容,如果未能解决你的问题,请参考以下文章

如何在 express node.js POST 请求中接收 JSON?

如何从 node.js Express 发送 POST 请求?

如何发送 POST 请求以响应 node.js Express?

Node.js + Express 接口请求(GETPOSTPUT)事例

解析来自 Node js + 车把的 POST 请求

node.js处理post请求