Html 如何在 node.js 中处理 POST 请求
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15427220/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to handle POST request in node.js
提问by Sau
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.
我正在尝试处理发送到我的 node.js 服务器的 post 请求。名称为 server.js 的 JavaScript 文件在浏览器上显示一个表单。我想在表单值发布到 node.js 后端后访问它们。
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:
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/');
回答by L0j1k
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.
我将使用您提供的代码,并提供比您的问题中涵盖的内容更彻底的答案,以适应“遥远的未来”中的人们。我还将提供一个使用“Vanilla JS”(http://www.vanilla-js.com/)的答案,因为我认为当您尝试了解其工作原理时,有太多潮人会说“使用框架”。我认为他们这样做的原因是因为有人告诉他们在学习如何工作时“使用框架”。因为他们不是黑客,他们不在乎尝试和理解这个过程,所以很多时候他们中的许多人不知道如何在没有框架的情况下自己做到这一点(因此无处不在的“使用框架”)。通过了解什么,您将成为更好的黑客
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.
既然您希望通过您输出的表单接受 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);
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.
首先,您将添加 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;
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.)
(我通常将这个函数的参数缩短为“req”和“res”,但这只是我的偏好。)
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.
这里我包含了一个简单的路由示例。在这种情况下,我们让我们的服务器侦听对“favicon.ico”的请求——几乎所有主要浏览器对网页的所有初始请求都会发出这个请求。此文件是您可以在您访问的每个网页的选项卡中看到的小图标。出于我们的目的,我们不需要提供网站图标,但我们将处理它的入站请求以显示一些基本的路由机制。
} 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.
如果您的访问者使用默认的 GET 方法将他们的浏览器指向您服务器上的任何其他资源(除了我们刚刚处理的“favicon.ico”),我们将为他们提供表单。
} 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.
否则,如果您的访问者将 POST 指向您的服务器,则他们很可能已经提交了他们通过上一个 GET 请求检索到的表单。
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.
在这里,我们正在侦听名为“/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);
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.
这可能看起来有点令人困惑,但我保证不会。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>');
});
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).
这是我们使用表单数据的地方。由于 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>');
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.
继续我们的路由示例,我们在这里所做的是在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);
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).
我希望您(和其他任何人)可以看到,使用 Node 的内置模块而不是依赖外部第三方库(如 Express)在 Node 中做事并不是什么深奥的,甚至有点困难的过程。这些库在世界上占有一席之地,但不要随波逐流:对您的代码做出明智的决定,因为归根结底,您是负责它的人(而不是 Stack Overflow 上的某些人)。