47

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

1 回答 1

157

我将使用您提供的代码,并提供比您的问题中所涵盖的内容更全面的答案,以适应遥远的未来中的人们。我还将提供一个使用“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 的内置“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) {

(我通常将此函数的参数缩短为“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”的请求——该请求与几乎所有主要浏览器对网页的初始请求一起发出。该文件是您可以在您访问的每个网页的选项卡中看到的小图标。出于我们的目的,我们不需要提供 favicon,但我们将处理入站请求以显示一些基本的路由机制。

    } 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)来做事并不是什么深奥甚至有点困难的过程。这些库在世界上占有一席之地,但不要随波逐流:对您的代码做出明智的决定,因为归根结底,您是负责它的人(而不是 Stack Overflow 上的某些人)。

于 2013-10-04T14:36:30.980 回答