我们先将这个对象引入到主文件index.js中:复制代码 代码如下:var server = require;var
router = require;var requestHandlers = require;

为了使整个过程非阻塞,Node.js会将POST数据拆分成很多小的数据块,然后通过触发特定的事件,将这些小数据块传递给回调函数。这里的特定的事件有data事件以及end事件。

重启服务器之后,通过访问

handler)

轻松创建nodejs服务器(9):实现非阻塞操作,nodejs服务器

我们要将response对象(从服务器的回调函数onRequest()获取)通过请求路由传递给请求处理程序。随后,处理程序就可以采用该对象上的函数来对请求作出响应。

我们先对server.js做出修改:

复制代码 代码如下:

var http = require(“http”);
var url = require(“url”);
function start(route, handle) {
  function onRequest(request, response) {
 var pathname = url.parse(request.url).pathname;
 console.log(“Request for ” + pathname + ” received.”);
 route(handle, pathname, response);
  }
  http.createServer(onRequest).listen(8888);
  console.log(“Server has started.”);
}
exports.start = start;

我们将response对象作为第三个参数传递给route()函数,并且,我们将onRequest()处理程序中所有有关response的函数调都移除,因为我们希望这部分工作让route()函数来完成。

接下来修改 router.js:

复制代码 代码如下:

function route(handle, pathname, response) {
  console.log(“About to route a request for ” + pathname);
  if (typeof handle[pathname] === ‘function’) {
 handle[pathname](response);
  } else {
 console.log(“No request handler found for ” + pathname);
 response.writeHead(404, {“Content-Type”: “text/plain”});
 response.write(“404 Not found”);
 response.end();
  }
}
exports.route = route;

同样的模式:相对此前从请求处理程序中获取返回值,这次取而代之的是直接传递response对象。
如果没有对应的请求处理器处理,我们就直接返回“404”错误。

接下来修改requestHandler.js:

复制代码 代码如下:

var exec = require(“child_process”).exec;
function start(response) {
  console.log(“Request handler ‘start’ was called.”);
  exec(“ls -lah”, function (error, stdout, stderr) {
 response.writeHead(200, {“Content-Type”: “text/plain”});
 response.write(stdout);
 response.end();
  });
}
 
function upload(response) {
  console.log(“Request handler ‘upload’ was called.”);
  response.writeHead(200, {“Content-Type”: “text/plain”});
  response.write(“Hello Upload”);
  response.end();
}
 
exports.start = start;
exports.upload = upload;

我们的处理程序函数需要接收response参数,为了对请求作出直接的响应。
start处理程序在exec()的匿名回调函数中做请求响应的操作,而upload处理程序仍然是简单的回复“Hello
World”,只是这次是使用response对象而已。

如果想要证明/start处理程序中耗时的操作不会阻塞对/upload请求作出立即响应的话,可以将requestHandlers.js修改为如下形式:

复制代码 代码如下:

var exec = require(“child_process”).exec;
function start(response) {
  console.log(“Request handler ‘start’ was called.”);
  exec(“find /”,
      { timeout: 10000, maxBuffer: 20000*1024 },
      function (error, stdout, stderr) {
  response.writeHead(200, {“Content-Type”: “text/plain”});
  response.write(stdout);
  response.end();
      }
  );
}
 
function upload(response) {
  console.log(“Request handler ‘upload’ was called.”);
  response.writeHead(200, {“Content-Type”: “text/plain”});
  response.write(“Hello Upload”);
  response.end();
}
 
exports.start = start;
exports.upload = upload;

这样一来,当请求

我们要将response对象(从服务器的回调函数onRequest()获取)通过请求路由传递给请…

在现在的实现下,路由过程会在路由模块中“结束”,并且路由模块并不是真正针对请求“采取行动”的模块,否则当我们的应用程序变得更为复杂时,将无法很好地扩展。

复制代码 代码如下:var querystring =
require;function start {console.log(“Request handler ‘start’ was
called.”);var body = ”+ ‘

复制代码 代码如下:var http = require;var
url = require;function start {function onRequest { var pathname =
url.parse.pathname; console.log(“Request for ” + pathname + ”
received.”); route(handle, pathname, response,
request);}http.createServer.listen;console.log(“Server has
started.”);}exports.start = start;

当用户请求*

用户可以选择一个图片并提交表单,随后文件将被上传到*
模块分析

server.start;在这里,我们传递的函数依旧什么也没做。

上述代码做了三件事情:
首先,我们设置了接收数据的编码格式为UTF-8,然后注册了“data”事件的监听器,用于收集每次接收到的新数据块,并将其赋值给postData
变量,最后,我们将请求路由的调用移到end事件处理程序中,以确保它只会当所有数据接收完毕后才触发,并且只触发一次。我们同时还把POST数据传递给请求路由,因为这些数据,请求处理程序会用到。

我们队requestHandlers.js进行一些修改:

– 由于提供web页面,因此需要HTTP服务器

对于不同的请求,根据请求的URL,服务器给予不同的响应,因为需要一个路由,用于把请求对应到请求处理程序(request

‘+ ”+ ”+ ”+ ”+ ”+ ”;

接下来在/upload页面,展示用户输入的内

要做的是确保该文件保存成/tmp/test.png。

路由还应该能处理post数据,并且把数据封装成更友好的格式传递给请求处理程序,因为需要请求数据处理功能。

当请求被服务器接收并通过路由传递之后,需要可以对其进行处理,因为我们需要最终的请求处理程序。

  • 最后,用户需要上传图片,所以我们需要上传处理功能。

#### 开始构建
##### 一、http服务器
首先创建一个用于启动我们的应用的主文件和一个保存着我们http服务器代码的模块。此模块为`server.js`
“`
var http = require(‘http’);

function start(){
function onRequest(request,response){
response.writeHead(200,{“Content-Type”:”text/plain”});
response.write(“hello world”);
response.end();
}
http.createServer(onRequest).listen(8000);

console.log(‘done’);
}

exports.start = start;

“`
接下来我们建立一个主入口文件为`index.js`
“`
var server = require(‘./server.js’);
server.start();
“`
再一次运行`node index.js`,也可以在页面上看到hello world。

##### 二、建立路由
我们要为路由提供请求的URL和其他需要的GET以及POST参数,随后路由需要根据这些数据来执行相应的代码。
所以我们需要查看HTTP请求,从中提取出请求的URL以及GET/POST参数。暂时我们将这一功能放在服务器模块。我们需要的所有的数据都会包含在request对象中,但是为了解析这些数据我们需要额外的Node.js模块,分别是`url`和`querystring`
。它们的作用如下图所示。
“`
url.parse(string).query
|
url.parse(string).pathname |
| |
| |



| |
| |
querystring(string)[“foo”] |
|
querystring(string)[“hello”]
“`
这个时候我们需要修改`server.js`文件来获取这些参数,然后将这些参数传入到路由中。首先获取`pathname`,在`server.js`的onRequest函数中添加以下代码
`var pathname = url.parse(request.url).pathname;`
到这里我们可以区分出不同的URL了,但是对不同的URL请求还没有设置不同的请求处理程序,首先我们来为不同的URL写一个路由程序为router.js。
“`
function route(pathname){
console.log(‘you have goto’ + pathname);
}
exports.route = route;
“`
这里采用的是在`server.js`中引入`router.js`然后将`router.route`传入`server.start()`,可以看到index.js的代码如下:
“`
var server = require(‘./server.js’);
var router = require(‘./router.js’);

server.start(router.route);
“`
`server.js`的代码如下:
“`
var http = require(‘http’);
var url = require(‘url’);

function start(route){
function onRequest(request,response){
var pathname = url.parse(request.url).pathname;
route(pathname);
response.writeHead(200,{“Content-Type”:”text/plain”});
response.write(“hello world”);
response.end();
}
http.createServer(onRequest).listen(8000);

console.log(‘done’);
}

exports.start = start;
“`
其实我们这里用到了函数式编程的思想,因为我们可以直接在`server.js`直接引入`router.js`模块,然后调用router对象中的route函数,但是这里并没有这样做,是因为`server.js`并不是真正的需要`router.js`而仅仅是需要它来做一个动作,所以我们并不需要真正的引入它。

##### 三、请求处理程序
我们首先建立请求处理程序requesthandler.js模块:
“`
function start() {
console.log(“Request handler ‘start’ was called.”);
}

function upload() {
console.log(“Request handler ‘upload’ was called.”);
}
exports.start = start;
exports.upload = upload;
“`
在这个模块中先定义两种不同的处理方法,然后将这两个方法导出。然后在`index.js`模块中引入`requesthandler.js`模块。并设置一个对象用来保存相对应的方法,`index.js`如下:
“`
var server = require(‘./server.js’);
var router = require(‘./router.js’);
var requestHandlers = require(“./requestHandlers”);
var handle = {}
handle[“/”] = requestHandlers.start;
handle[“/start”] = requestHandlers.start;
handle[“/upload”] = requestHandlers.upload;

server.start(router.route,handle);
“`
这样我们就可以将handle直接传递给start函数,在server.js中再将handle对象传入route函数中,这样就可以做到让router.js来决定执行哪一个请求处理函数。
“`
var http = require(‘http’);
var url = require(‘url’);

function start(route,handle){
function onRequest(request,response){
var pathname = url.parse(request.url).pathname;
route(handle,pathname);
response.writeHead(200,{“Content-Type”:”text/plain”});
response.write(“hello world”);
response.end();
}
http.createServer(onRequest).listen(8000);

console.log(‘done’);
}

exports.start = start;

“`
router.js中先判断一下是否handle[pathname] ===
‘function’如果是就执行对应的方法。
“`
function route(handle,pathname){
console.log(‘you have goto’ + pathname);
if (typeof handle[pathname] === ‘function’) {
handle[pathname]();
}else {
console.log(‘no request handler found for’ + pathname);
}
}

exports.route = route;
“`
这里已经实现不同的url能有不同请求处理程序,但是还没有在页面上进行响应。我们可以直接采用`return
()`的方法来返回响应的内容,但是这种方法在未来如果有阻塞操作的时候就会发生问题,所以我们采取另外一种方式。将*response*对象(从服务器的回调函数*onRequest()*获取)通过请求路由传递给请求处理程序。
随后,处理程序就可以采用该对象上的函数来对请求作出响应。首先在`server.js`上将response传递给route函数,将所有response操作都删除。把这些response操作都放在router.js中。
“`
var http = require(‘http’);
var url = require(‘url’);
function start(route,handle){
function onRequest(request,response){
var pathname = url.parse(request.url).pathname;
console.log(“Request for ” + pathname + ” received.”)
route(handle,pathname,response);
}
http.createServer(onRequest).listen(8000);
console.log(‘done’);
}
exports.start = start;
“`
在router.js中,也是将传入的response对象再传递到handle[pathname]对应的函数中。
“`
function route(handle,pathname,response){
console.log(‘you have goto’ + pathname);
if (typeof handle[pathname] === ‘function’) {
handle[pathname](response);
}else {
response.writeHead(404, {“Content-Type”: “text/plain”});
response.write(“404 Not found”);
response.end();
console.log(‘no request handler found for’ + pathname);
}
}
exports.route = route;
“`
在requesthandler.js中接收传入的response对象,然后引入了一个node.js中一个模块,*child_process*。之所以用它,是为了实现一个既简单又实用的非阻塞操作:*exec()*。exec()的作用就是用Node.js来执行一个shell命令。在下面的代码中,我们用它来获取当前目录下所有的文件(“ls
-lah”),然后,当*/start*URL请求的时候将文件信息输出到浏览器中。
“`
var exec = require(“child_process”).exec;
function start(response) {
console.log(“Request handler ‘start’ was called.”);
exec(“ls -lah”, function (error, stdout, stderr) {
response.writeHead(200, {“Content-Type”: “text/plain”});
response.write(stdout);
response.end();
});
}
function upload(response) {
console.log(“Request handler ‘upload’ was called.”);
response.writeHead(200, {“Content-Type”: “text/plain”});
response.write(“Hello Upload”);
response.end();
}
exports.start = start;
exports.upload = upload;
“`
这里用了exec()一个简单的非阻塞操作,把response的操作放在它的回调函数中。这样就达到了我们要求非阻塞的目的。

##### 四、添加交互(处理post请求)
我们需要实现的是用户选择一个文件,上传该文件,然后在浏览器中看到上传的文件。
为了保持简单,我们假设用户只会上传图片,然后我们应用将该图片显示到浏览器中。
首先我们在请求/start时会返回一个文本框和一个提交按钮,当用户点击提交按钮的时候使用post方法,此时页面显示的是用户在文本框里输入的文字。
“`
function start(response) {
console.log(“Request handler ‘start’ was called.”);

var body = ‘<html>’+
‘<head>’
‘<meta http-equiv=”Content-Type” content =”text/html” >’+
‘<charset=UTF-8>’+
‘</head>’+
‘<body>’+
‘<form action=”/upload” method=”post”>’+
‘<textarea name=”text” rows=”20″ cols=”60″ ></textarea>’+
‘<input type=”submit” value=”submit text” />’+
‘</form>’+
‘</body>’+
‘</html>’

response.writeHead(200, {“Content-Type”: “text/html”});
response.write(body);
response.end();
}

function upload(response) {
console.log(“Request handler ‘upload’ was called.”);
response.writeHead(200, {“Content-Type”: “text/plain”});
response.write(“Hello Upload”);
response.end();
}

exports.start = start;
exports.upload = upload;
“`
这个时候已经能够在请求/start时返回相应的页面,但是还没有为/upload添加效果。我们采用异步回调来实现非阻塞地处理post请求的数据。因为post请求一般都比较”重“——用户可能会输入大量的内容,用阻塞的方式处理大量数据请求必然会导致用户操作的阻塞。所以在node.js中会将post数据拆分成很多小的数据块,然后通过触发特定的事件,将这些小数据块传递给回调函数。这里特定的事件是data事件(表示新的小数据块到达了),以及end事件(表示所有的数据已经接收完毕。我们需要做的是告诉Node.js当这些事件触发的时候,回调哪些函数。所以我们的实现思路是在server.js中设置data和end的回调函数,在data事件回调中收集所有的post数据,当接收到所有数据,触发end事件后,其回调函数调用请求路由,并将数据传递给它,然后请求路由将该数据传递给请求处理程序。所以在server.js中设置data和end事件。
“`
var http = require(‘http’);
var url = require(‘url’);

function start(route,handle){
function onRequest(request,response){
var postDate = ”;
var pathname = url.parse(request.url).pathname;
console.log(“Request for ” + pathname + ” received.”);
request.setEncoding(“utf8”);
request.addListener(‘data’,function(postDateChunk){
postDate +=postDateChunk;
console.log(‘received post data chunk’+postDateChunk)
});
request.addListener(‘end’,function(){
route(handle,pathname,response,postDate);
})
}
http.createServer(onRequest).listen(8000);

console.log(‘done’);
}

exports.start = start;
“`
然后在router.js中传入postDate,并且将postData传入请求处理程序中,代码如下:
“`
function route(handle,pathname,response,postDate){
console.log(‘you have goto’ + pathname);
if (typeof handle[pathname] === ‘function’) {
handle[pathname](response,postDate);
}else {
response.writeHead(404, {“Content-Type”: “text/plain”});
response.write(“404 Not found”);
response.end();
console.log(‘no request handler found for’ + pathname);
}
}

exports.route = route;
“`
最后修改requesthandler.js将upload函数添加postDate参数,然后把数据展示出来,代码如下:
“`
function upload(response,postData) {
console.log(“Request handler ‘upload’ was called.”);
response.writeHead(200, {“Content-Type”: “text/plain”});
response.write(postData);
response.end();
}
“`
这个时候就实现了在/start时显示文本框和提交按钮,在提交之后只显示输入的内容。但是我们是把请求的整个消息体传递给了请求路由和请求处理程序,但是我们只应该把post数据中的text部分提取出来。所以我们引入一个新的模块querystring,在requesthandler.js中改变response.write为
`response.write(querystring.parse(postDate).text)`。这就完整的完成的处理post请求的功能。

##### 添加交互(处理文件上传)
首先我们要引入一个外部的模块formidable,它能够解析上传的文件数据也就是处理post数据。对于formidable的介绍在本文末尾。接下来我们先安装这个模块。
`npm install formidable`
由于我们要把上传的文件显示到浏览器中,我们要先把它保存在本地的磁盘中,再读出显示到浏览器中。我们就需要使用fs模块将文件读取到服务器中。然后添加一个/showURL请求处理程序,该程序直接将读取并保存在/tmp/test.png的文件展示在浏览器中。在requesthandler.js新增的代码如下:
“`
var fs = require(‘fs’);
function show(response,postData) {
console.log(“Request handler ‘show’ was called.”);
fs.readFile(“/tmp/test.png”,”binary”,function(error,file){
if (error) {
response.writeHead(500,{“Content-Type”:”text/plain”});
response.write(error + “\n”);
response.end();
}else {
response.writeHead(200,{“Content-Type”:”image/png”});
response.write(file,”binary”);
response.end();
}
})
}
exports.show = show;
“`
这个时候我们就可以打开localhost:8888/show就可以看到我们保存在/tmp/test.png文件。最后我们要实现的是先上传文件,然后再显示,所以要做的事情有

  • 1、在/start表单中去掉文本框,新增一个添加文件上传元素。
    “`
    ‘<form
    action=”/upload”method=”post”enctype=”multipart/form-data”>’+
    ‘<input type=”file” name=”upload”>’
    “`

    enctype属性规定在发送到服务器之前应该如何对表单数据进行编码,如果设置为multipart/form-data就表示不对字符进行编码,**并且规定在使用包含文件上传控件的表单时,必须使用该值**。

    2、将node-formidable整合到我们的upload请求处理程序中,用于将上传的图片保存到/tmp/test.png
    我们需要在upload处理程序中对上传的文件进行处理,这样的话,就需要将request对象传递给node-formidable的form.parse函数,但是我们现在只有response和postDate,所以我们这里可以改变原来的方法,不再传递postData,直接传递request对象。首先移除对postData的处理以及request.setEncoding(这部分node-formidable自身会处理),并传递request对象给路由。

“`
var http = require(‘http’);
var url = require(‘url’);
var formidable = require(‘formidable’);

function start(route,handle){
function onRequest(request,response){
var pathname = url.parse(request.url).pathname;
console.log(“Request for ” + pathname + ” received.”);
route(handle,pathname,response,request);

}
http.createServer(onRequest).listen(8000);

console.log(‘done’);
}

exports.start = start;
“`

然后在router.js中传入request对象
“`
function route(handle,pathname,response,request){
console.log(‘you have goto’ + pathname);
if (typeof handle[pathname] === ‘function’) {
handle[pathname](response,request);
}else {
response.writeHead(404, {“Content-Type”: “text/plain”});
response.write(“404 Not found”);
response.end();
console.log(‘no request handler found for’ + pathname);
}
}

exports.route = route;
“`
现在就可以在upload函数中使用request对象了

  • 3、将上传的图片内嵌到/uploadURL输出的html中。

“`
var querystring = require(‘querystring’);
var fs = require(‘fs’);

function start(response) {
console.log(“Request handler ‘start’ was called.”);

var body = ‘<html>’+
‘<head>’+
‘<meta http-equiv=”Content-Type” content =”text/html” >’+
‘<charset=UTF-8>’+
‘</head>’+
‘<body>’+
‘<form action=”/upload” method=”post”
enctype=”multipart/form-data”>’+
‘<input type=”file” name=”upload”>’
‘<input type=”submit” value=”submit text” />’+
‘</form>’+
‘</body>’+
‘</html>’

response.writeHead(200, {“Content-Type”: “text/html”});
response.write(body);
response.end();
}

function upload(response,request) {
console.log(“Request handler ‘upload’ was called.”);
var form = new formidable.IncomingForm();
console.log(‘about to parse’);
form.parse(request,function(error,fields,flies){
console.log(‘parsing done’);
fs.renameSync(files.upload.path,”/tmp/test.png”);
response.writeHead(200, {“Content-Type”: “text/plain”});
response.write(“received image :<br/>”);
response.write(“<img src=’/show/’>”);
response.end();
})

}
function show(response,request) {
console.log(“Request handler ‘show’ was called.”);
fs.readFile(“/tmp/test.png”,”binary”,function(error,file){
if (error) {
response.writeHead(500,{“Content-Type”:”text/plain”});
response.write(error + “\n”);
response.end();
}else {
response.writeHead(200,{“Content-Type”:”image/png”});
response.write(file,”binary”);
response.end();
}
})
}
exports.start = start;
exports.upload = upload;
exports.show = show;
“`
终于完成了!
##### 遇到问题
当我在运行最后的结果时,遇到一个错误是cross-device link not
permitted,意思是跨磁盘操作是不允许的。当我把tmp/test.png创建到C盘下就不会报错。

##### 附录: node-formidable模块
formidable模块是专门用来解析表单数据的,特别是上传的数据。下面来看一下formidable的常用API以及使用方法。

  • 创建一个新的上传表单
    `var form = new formidable.IncomingForm()`
  • 设置编码

    `form.encoding = ‘utf-8’`

    设置传入的文件的目录,你也可以通过fs.rename()来移动它,默认的是os.tmpDir();
    `form.uploadDir = “/my/dir”`

  • 转换请求中包含的表单数据,callback会包含所有字段域和文件信息。
    `form.parse(request,function(err,fields,files){
    //…
    })`

##### 参考资料
[node入门]() 强烈推荐!
[node-formidable详解]()
[node.js API]()

好,了解了这种不好的实现方式之后,我们接下来来介绍如何以正确的方式让请求处理程序对浏览器请求作出响应。

我们通过在request对象上注册监听器 来实现。这里的
request对象是每次接收到HTTP请求时候,都会把该对象传递给onRequest回调函数。

‘+ ”+ ”+ ”+ ”+ ”+ ”;response.writeHead(200, {“Content-Type”:
“text/html”});response.write;}function upload {console.log(“Request
handler ‘upload’ was called.”);response.writeHead(200, {“Content-Type”:
“text/plain”});response.write(“You’ve sent the text: “+
querystring.parse;response.end();}function show {console.log(“Request
handler ‘show’ was called.”);fs.readFile(“/tmp/test.png”, “binary”,
function { if { response.writeHead(500, {“Content-Type”: “text/plain”});
response.write; response.end(); } else { response.writeHead(200,
{“Content-Type”: “image/png”}); response.write;
response.end;}exports.start = start;exports.upload = upload;exports.show
= show;

####一个基于nodejs的web应用
功能要求

一个完整的基于Node.js的web应用

好了,以上就是关于处理POST数据的全部内容。

复制代码 代码如下:function upload {
console.log(“Request handler ‘upload’ was called.”); var form = new
formidable.IncomingForm(); console.log;form.uploadDir =
“tmp”;form.parse(request, function { console.log;
fs.renameSync(files.upload.path, “/tmp/test.png”);
response.writeHead(200, {“Content-Type”: “text/html”});
response.write(“received image:
“); response.write(“威尼斯城真人赌钱网站 1“); response.end;
}我们增加了一句 form.uploadDir =
“tmp”,现在重启服务器,再执行上传操作,问题完美解决。

– 用户可以通过浏览器使用应用。

在我们所要构建的应用中,这意味着来自/start和/upload的请求可以使用不同的代码来处理。稍后我们将看到这些内容是如何整合到一起的。

我们在start事件处理器里添加代码,requestHandlers.js修改如下:

‘+ ”+ ”+ ”+ ”+ ”+ ”;response.writeHead(200, {“Content-Type”:
“text/html”});response.write;}function upload {console.log(“Request
handler ‘upload’ was called.”);response.writeHead(200, {“Content-Type”:
“text/plain”});response.write(“You’ve sent the text: “+
querystring.parse;response.end();}function show {console.log(“Request
handler ‘show’ was called.”);fs.readFile(“/tmp/test.png”, “binary”,
function { if { response.writeHead(500, {“Content-Type”: “text/plain”});
response.write; response.end(); } else { response.writeHead(200,
{“Content-Type”: “image/png”}); response.write;
response.end;}exports.start = start;exports.upload = upload;exports.show
= show;

当我准备开始写我的第一个“真正的”Node.js应用的时候,我不但不知道怎么写Node.js代码,也不知道怎么组织这些代码。
我应该把所有东西都放进一个文件里吗?网上有很多教程都会教你把所有的逻辑都放进一个用Node.js写的基础HTTP服务器里。但是如果我想加入更多的内容,同时还想保持代码的可读性呢?

我们要做的是:用户选择一个文件,上传该文件,然后在浏览器中看到上传的文件。

 将上传的图片内嵌到/uploadURL输出的HTML中

创建 index.js 文件并写入以下内容:复制代码 代码如下:var server = require;

复制代码 代码如下:function route(handle,
pathname, response, postData) {console.log(“About to route a request for
” + pathname);if (typeof handle[pathname] === ‘function’) {
handle[pathname];} else { console.log(“No request handler found for “

接下来修改router.js,这次要传递request对象:

function show { console.log(“Request handler ‘show’ was called.”);
fs.readFile(“/tmp/test.png”, “binary”, function { if {
response.writeHead(500, {“Content-Type”: “text/plain”}); response.write;
response.end(); } else { response.writeHead(200, {“Content-Type”:
“image/png”}); response.write; response.end;}

我们可以使用此前介绍过的querystring模块来实现:

 在/start表单中添加一个文件上传元素

response.writeHead(200, {“Content-Type”: “text/html”}); response.write;}

  • pathname); response.writeHead(404, {“Content-Type”: “text/plain”});
    response.write; response.end();}}exports.route =
    route;然后,在requestHandlers.js中,我们将数据包含在对upload请求的响应中:

复制代码 代码如下:var querystring =
require,fs = require;function start {console.log(“Request handler
‘start’ was called.”);var body = ”+ ‘

让我们从让请求处理程序返回需要在浏览器中显示的信息开始。我们需要将requestHandler.js修改为如下形式:复制代码 代码如下:function start() {
console.log(“Request handler ‘start’ was called.”); return “Hello
Start”;}

复制代码 代码如下:function start
{console.log(“Request handler ‘start’ was called.”);var body = ”+ ‘

  • pathname); response.writeHead(404, {“Content-Type”: “text/html”});
    response.write; response.end();}}exports.route = route;

然后,出现了Node.js,服务端的JavaScript,这有多酷啊?

下一节,我们将实现图片上传的功能。

复制代码 代码如下:var server =
require;var router = require;var requestHandlers = require;var handle =
{}handle[“/”] = requestHandlers.start;handle[“/start”] =
requestHandlers.start;handle[“/upload”] =
requestHandlers.upload;handle[“] =
requestHandlers.show;server.start;

1.我们需要提供Web页面,因此需要一个HTTP服务器2.对于不同的请求,根据请求的URL,我们的服务器需要给予不同的响应,因此我们需要一个路由,用于把请求对应到请求处理程序3.当请求被服务器接收并通过路由传递之后,需要可以对其进行处理,因此我们需要最终的请求处理程序4.路由还应该能处理POST数据,并且把数据封装成更友好的格式传递给请求处理入程序,因此需要请求数据处理功能5.我们不仅仅要处理URL对应的请求,还要把内容显示出来,这意味着我们需要一些视图逻辑供请求处理程序使用,以便将内容发送给用户的浏览器6.最后,用户需要上传图片,所以我们需要上传处理功能来处理这方面的细节我们先来想想,使用PHP的话我们会怎么构建这个结构。一般来说我们会用一个Apache
HTTP服务器并配上mod_php5模块。从这个角度看,整个“接收HTTP请求并提供Web页面”的需求根本不需要PHP来处理。

‘+ ”+ ”+ ”+ ”+ ”+ ”;response.writeHead(200, {“Content-Type”:
“text/html”});response.write;}function upload {console.log(“Request
handler ‘upload’ was called.”);response.writeHead(200, {“Content-Type”:
“text/plain”});response.write;response.end();}exports.start =
start;exports.upload = upload;

我们还需要将这新的请求处理程序,添加到index.js中的路由映射表中:

‘+ ”+ ”+ ”+ ”+ ”+ ”;

目前为止,我们做的服务器没有实际的用处,接下来我们开始实现一些实际有用的功能。

我们找到requestHandlers.js的 upload函数,将它做一些修改:

好了,“废话”不多说了,马上开始我们第一个Node.js应用:“Hello World”。

我们来改一下 router.js:

第一项很简单。只需要在HTML表单中,添加一个multipart/form-data的编码类型,移除此前的文本区,添加一个文件上传组件,并将提交按钮的文案改为“Upload
file”即可。 如下requestHandler.js所示:

route(handle, pathname, response); }

  • ” received.”); request.setEncoding; request.addListener(“data”,
    function { postData += postDataChunk; console.log(“Received POST data
    chunk ‘”+ postDataChunk + “‘.”); }); request.addListener { route(handle,
    pathname, response, postData);
    });}http.createServer.listen;console.log(“Server has
    started.”);}exports.start = start;

好,最后我们要的就是:

目前,我们的HTTP服务器需要导出的功能非常简单,因为请求服务器模块的脚本仅仅是需要启动服务器而已。

‘+ ”+ ”+ ”+ ”+ ”+ ”;response.writeHead(200, {“Content-Type”:
“text/html”});response.write;}function upload {console.log(“Request
handler ‘upload’ was called.”);response.writeHead(200, {“Content-Type”:
“text/plain”});response.write(“You’ve sent the text: “+
querystring.parse;response.end();}exports.start = start;exports.upload =
upload;

在执行图片上传的过程中,有的人可能会遇到这样的问题:

我们创建了服务器,并且向创建它的方法传递了一个函数。无论何时我们的服务器收到一个请求,这个函数就会被调用。

复制代码 代码如下:function start
{console.log(“Request handler ‘start’ was called.”);var body = ”+ ‘

接下来,我们要完成第二步,我们从server.js开始 ——
移除对postData的处理以及request.setEncoding
(这部分node-formidable自身会处理),转而采用将request对象传递给请求路由的方式:

在第二个浏览器窗口的地址栏中输入
同样的,先不要打开它!

复制代码 代码如下:var http = require;var
url = require;function start {function onRequest { var postData = “”;
var pathname = url.parse.pathname; console.log(“Request for ” + pathname

复制代码 代码如下:function route(handle,
pathname, response, request) {console.log(“About to route a request for
” + pathname);if (typeof handle[pathname] === ‘function’) {
handle[pathname];} else { console.log(“No request handler found for “

exports.route =
route;然后,在requestHandlers.js中,我们将数据包含在对upload请求的响应中:复制代码 代码如下:function start {
console.log(“Request handler ‘start’ was called.”);

通过在浏览器中访问

我们先来解决一个问题:如何才能在浏览器中显示保存在本地硬盘中的文件?

那么我们要怎么传递这些请求处理程序呢?别看现在我们只有2个处理程序,在一个真实的应用中,请求处理程序的数量会不断增加,我们当然不想每次有一个新的URL或请求处理程序时,都要为了在路由里完成请求到处理程序的映射而反复折腾。除此之外,在路由里有一大堆if
request == x then call handler y也使得系统丑陋不堪。

接下来我们要实现当用户提交表单时,触发/upload请求处理程序处理POST请求。

复制代码 代码如下:var querystring =
require,fs = require;function start {console.log(“Request handler
‘start’ was called.”);var body = ”+ ‘

到目前为止,我们的应用已经可以通过应用各层之间传递值的方式(请求处理程序
-> 请求路由 -> 服务器)将请求处理程序返回的内容传递给HTTP服务器。

我们把代码放在服务器里,server.js修改如下:

照成这个问题的原因我猜测是由于磁盘分区导致的,要解决这个问题就需要改变formidable的默认零时文件夹路径,保证和目标目录处于同一个磁盘分区。

好,最后我们要的就是:

首先我们需要一个文本区供用户输入内容,然后通过POST请求提交给服务器。

复制代码 代码如下:var querystring =
require,fs = require,formidable = require;function start
{console.log(“Request handler ‘start’ was called.”);var body = ”+ ‘

function upload { console.log(“Request handler ‘upload’ was called.”);
response.writeHead(200, {“Content-Type”: “text/plain”});
response.write(“You’ve sent the text: “+ querystring.parse;
response.end();}

‘+ ”+ ”+ ”+ ”+ ”+ ”;response.writeHead(200, {“Content-Type”:
“text/html”});response.write;}function upload {console.log(“Request
handler ‘upload’ was called.”);response.writeHead(200, {“Content-Type”:
“text/plain”});response.write(“You’ve sent: ” +
postData);response.end();}exports.start = start;exports.upload = upload;

本节我们将实现,用户上传图片,并将该图片在浏览器中显示出来。

server.start;重启服务器之后,通过访问

我们最后要做的是:
当前我们是把请求的整个消息体传递给了请求路由和请求处理程序。我们应该只把POST数据中,我们感兴趣的部分传递给请求路由和请求处理程序。在我们这个例子中,我们感兴趣的其实只是text字段。

这里我们要用到的外部模块是Felix
Geisendörfer开发的node-formidable模块。它对解析上传的文件数据做了很好的抽象。

下面就来看看我们的router.js:复制代码
代码如下:function route(handle, pathname, response) { console.log(“About
to route a request for ” + pathname); if (typeof handle[pathname] ===
‘function’) { handle[pathname]; } else { console.log(“No request
handler found for ” + pathname); response.writeHead(404,
{“Content-Type”: “text/plain”}); response.write; response.end(); }}

‘+ ”+ ”+ ”+ ”+ ”+ ”;response.writeHead(200, {“Content-Type”:
“text/html”});response.write;}function upload {console.log(“Request
handler ‘upload’ was called.”);var form = new
formidable.IncomingForm();console.log;form.parse(request, function {
console.log; fs.renameSync(files.upload.path, “/tmp/test.png”);
response.writeHead(200, {“Content-Type”: “text/html”});
response.write(“received image:
“); response.write(“威尼斯城真人赌钱网站 2“);
response.end;}function show {console.log(“Request handler ‘show’ was
called.”);fs.readFile(“/tmp/test.png”, “binary”, function { if {
response.writeHead(500, {“Content-Type”: “text/plain”}); response.write;
response.end(); } else { response.writeHead(200, {“Content-Type”:
“image/png”}); response.write; response.end;}exports.start =
start;exports.upload = upload;exports.show = show;

为了让效果更加明显,我们想象一个更耗时的命令: “find
/”,它在我机器上需要执行1分钟左右的时间,然而,尽管在请求处理程序中,我把“ls
-lah”换成“find /”,当打开/start URL的时候,依然能够立即获得HTTP响应 ——
很明显,当exec()在后台执行的时候,Node.js自身会继续执行后面的代码。并且我们这里假设传递给exec()的回调函数,只会在“find
/”命令执行完成之后才会被调用。

本文案例的图片文件存储在 /tmp文件夹中。

至少对我来说,需要一些功夫才能弄懂它。你如果还是不太确定的话就再去读读Felix的博客文章。

要安装这个外部模块,需在cmd下执行命令:复制代码 代码如下:npm install
formidable如果输出类似的信息就代表安装成功了:复制代码 代码如下:npm info build Success:
formidable@1.0.14安装成功后我们用request将其引入即可:复制代码 代码如下:var formidable =
require;这里该模块做的就是将通过HTTP
POST请求提交的表单,在Node.js中可以被解析。我们要做的就是创建一个新的IncomingForm,它是对提交表单的抽象表示,之后,就可以用它解析request对象,获取表单中需要的数据字段。

之后会载入一个漂亮的web页面,其内容为“empty”。怎么回事?

我们来添加

抛开技术,我们先来聊聊你以及你和JavaScript的关系。本章的主要目的是想让你看看,对你而言是否有必要继续阅读后续章节的内容。

做到这里,我们的服务器就全部完成了。

余下的篇幅,我们来探讨一个更有趣的问题:
当用户提交表单时,触发/upload请求处理程序处理POST请求的问题。

现在,request对象就可以在我们的upload请求处理程序中使用了。node-formidable会处理将上传的文件保存到本地/tmp目录中,而我们需

当我们与往常一样,运行它node server.js时,它会马上在命令行上输出“Server
has
started.”。当我们向服务器发出请求(在浏览器访问
),“Request received.”这条消息就会在命令行中出现。

接下来,我们把处理文件上传以及重命名的操作放到一起,如下requestHandlers.js所示:

function show { console.log(“Request handler ‘show’ was called.”);
fs.readFile(“/tmp/test.png”, “binary”, function { if {
response.writeHead(500, {“Content-Type”: “text/plain”}); response.write;
response.end(); } else { response.writeHead(200, {“Content-Type”:
“image/png”}); response.write; response.end;}

我们使用fs模块来将文件读取到服务器中。

到这里,我们可以将postData从服务器以及请求处理程序中移除了 ——
一方面,对于我们处理文件上传来说已经不需要了,另外一方面,它甚至可能会引发这样一个问题:
我们已经“消耗”了request对象中的数据,这意味着,对于form.parse来说,当它想要获取数据的时候就什么也获取不到了。


将node-formidable整合到我们的upload请求处理程序中,用于将上传的图片保存到/tmp/test.png

response.writeHead(200, {“Content-Type”: “text/plain”}); var content =
route response.write; response.end(); }

接下来,我们会介绍一种错误的使用非阻塞操作的方式。

现在我们来给onRequest()函数加上一些逻辑,用来找出浏览器请求的URL路径:

然而,要用非阻塞操作,我们需要使用回调,通过将函数作为参数传递给其他需要花时间做处理的函数(比方说,休眠10秒,或者查询数据库,又或者是进行大量的计算)。

在完成了对象的定义后,我们把它作为额外的参数传递给服务器,为此将server.js修改如下:复制代码 代码如下:var http = require;var url =
require;

我们不知道这件事情什么时候会发生,但是我们现在有了一个处理请求的地方:它就是我们传递过去的那个函数。至于它是被预先定义的函数还是匿名函数,就无关紧要了。

接下来,我们把处理文件上传以及重命名的操作放到一起,如下requestHandlers.js所示:复制代码 代码如下:var querystring = require, fs
= require, formidable = require;

function start { function onRequest { var pathname = url.parse.pathname;
console.log(“Request for ” + pathname + ” received.”);

response.writeHead(200, {“Content-Type”: “text/html”}); response.write;}

构建应用的模块

我刚刚提到了这样一个短语 ——
“正确的方式”。而事实上通常“正确的方式”一般都不简单。

那么,现在我们来创建一个用于启动我们的应用的主文件,和一个保存着我们的HTTP服务器代码的模块。

我们这里“ls
-lah”的操作其实是非常快的。这也是为什么回调函数也会很快的执行到 ——
不过,不管怎么说它还是异步的。

总结与展望

function upload() { console.log(“Request handler ‘upload’ was called.”);
return “Hello Upload”;}

本书状态

我是在读了Steve
Yegge的大作名词王国中的死刑之后理解函数编程。你也去读一读这本书吧,真的。这是曾给予我阅读的快乐的关于软件的书籍之一。

我们先就这样去实现,然后再来看为什么这不是一种很好的实现方式。

function route { console.log(“About to route a request for ” +
pathname);}

当我们使用 http.createServer
方法的时候,我们当然不只是想要一个侦听某个端口的服务器,我们还想要它在服务器收到一个HTTP请求的时候做点什么。

exports.route =
route;如你所见,这段代码什么也没干,不过对于现在来说这是应该的。在添加更多的逻辑以前,我们先来看看如何把路由和服务器整合起来。

请允许我再次脱离主题,在这里谈一谈函数式编程。

我们需要告诉Node.js当这些事件触发的时候,回调哪些函数。怎么告诉呢?
我们通过在request对象上注册监听器实现。这里的request对象是每次接收到HTTP请求时候,都会把该对象传递给onRequest回调函数。如下所示:复制代码 代码如下:request.addListener(“data”,
function { // called when a new chunk of data was received});

现在我们采用如下这种新的实现方式:相对采用将内容传递给服务器的方式,我们这次采用将服务器“传递”给内容的方式。
从实践角度来说,就是将response对象获取)通过请求路由传递给请求处理程序。
随后,处理程序就可以采用该对象上的函数来对请求作出响应。

JavaScript与你

但如果JavaScript的对象仅仅是键/值对的集合,它又怎么会拥有方法呢?好吧,这里的值可以是字符串、数字或者……函数!

‘+ ”+ ”+ ”+ ”+ ”+ ”;

这把我们的本地变量变成了一个拥有所有 http 模块所提供的公共方法的对象。

问题就在于,为了进行非阻塞工作,exec()使用了回调函数。

我们来把目标设定得简单点,不过也要够实际才行:

function start { console.log(“Request handler ‘start’ was called.”);

我们从server.js开始 —— 移除对postData的处理以及request.setEncoding
(这部分node-formidable自身会处理),转而采用将request对象传递给请求路由的方式:复制代码 代码如下:var http = require;var url =
require;

用这种方式,我们甚至不用给这个函数起名字,这也是为什么它被叫做 匿名函数

http.createServer.listen; console.log(“Server has started.”);}

用例

node-formidable官方的例子展示了这两部分是如何融合在一起工作的:复制代码 代码如下:var formidable = require,
http = require, sys = require;

不过,用Node.js就有这样一种实现方案:
函数传递。下面就让我们来具体看看如何实现。

return content;}

首先,我们来扩展一下服务器的start()函数,以便将路由函数作为参数传递过去:复制代码 代码如下:var http = require;var url =
require;

这到底是为什么呢?原因就是start()包含了阻塞操作。形象的说就是“它阻塞了所有其他的处理工作”。

你正在阅读的已经是本书的最终版。因此,只有当进行错误更正以及针对新版本Node.js的改动进行对应的修正时,才会进行更新。

function upload { console.log(“Request handler ‘upload’ was called.”);
response.writeHead(200, {“Content-Type”: “text/plain”});
response.write(“You’ve sent: ” + postData); response.end();}

exports.start =
start;相对此前从route()函数获取返回值的做法,这次我们将response对象作为第三个参数传递给route处理程序中所有有关response的函数调都移除,因为我们希望这部分工作让route()函数来完成。

我们现在就来谈谈怎么把server.js变成一个真正的Node.js模块,使它可以被我们的
index.js 主文件使用。

相关文章