关于javascript:如何在node.js中处理POST请求

How to handle POST request in node.js

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

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

server.js代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var http = require('http');

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

console.log('Server running at http://127.0.0.1:8124/');


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
var http = require('http');
var qs = require('querystring');
var formOutput = '<html><body>'
  + 'XYZ Repository Commit Monitor'
  + '<form method="post" action="inbound" enctype="application/x-www-form-urlencoded"><fieldset>'
  + '<label for="UserName">User Name:</label><input type="text" id="UserName" name="UserName" />'
  + '<label for="Repository">Repository:</label><input type="text" id="Repository" name="Repository" />'
  + '<label for="Branch">Branch:</label><input type="text" id="Branch" name="Branch" value="master" />'
  + '<input id="ListCommits" type="submit" value="List Commits" /></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>404</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>413</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>response</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>404</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>405</head><body>405: Method Not Supported</body></html>');
  }
}).listen(serverPort);
console.log('Server running at localhost:'+serverPort);

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

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

首先,您将添加Node的内置"查询字符串"模块来解析实际的表单数据。

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

我已将表单输出移到我们的服务器/路由/表单处理机制之上,因为逻辑更容易阅读。我还在这里移动了服务器侦听端口信息,因为您只需要在一个地方而不是在下面的地方更改它。

1
http.createServer(function (request, response) {

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

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

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

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

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

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

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

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

在这里,我们正在侦听名为"/ inbound"的入站请求,如果您发现上面的小细节,则是我们的HTML表单的"操作"。您可能知道,表单的"操作"告诉浏览器将表单数据发送到何处。

1
2
3
4
5
6
7
8
9
10
      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>413</head><body>413: Request Entity Too Large</body></html>');
        }
      });
      request.on('end', function() {
        var formData = qs.parse(requestBody);

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

1
2
3
4
5
6
7
        response.writeHead(200, {'Content-Type': 'text/html'});
        response.write('<!doctype html><html><head>response</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(例如"UserName"而不是"username")。当然,您可以使用此数据执行任何操作(请记住Node的事件循环和异步特性)。

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

为了继续我们的路由示例,我们在这里完成的工作包括在if()语句下面的一个catch-all,它向客户端发送一个通用的404"Not Found"回复到我们尚未处理的任何POST请求。

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

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

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

好。