node.js 实例
在这个小节中,我们将实现一个使用 node.js 开发的 HTTP 服务器,这个服务器提供 REST 方式的 API 来对客户端的数据进行处理,简单起见,这个服务器仅处理 POST 的数据,同时,这个服务器仅处理三种字符串处理操作:
- 回显(echo),将客户端发送的字符串原封不动的返回
- 转大写(upper),将客户端发送的字符串转换为大写并返回
- 转小写(lower),将客户端发送的字符串转换为小写并返回
访问方式为:
curl -X POST http://10.111.43.117:8080/upper -d "hello, world"
该服务器将返回”HELLO, WORLD”。 我们建立两个模块,httpserver.js 负责接受请求,并根据请求类型分发;而 actions.js 则 负责实际的请求处理(字符串转换)。 httpserver.js 的原型如下,加载 node.js 内置的 http 模块,然后调用 createServer,并 在 8080 端口监听:
var http = require("http");
http.createServer(function(request, response){
response.writeHead(200, {"Content-Type":"text/plain"});
response.write("httpserver");
response.end();
}).listen(8080);
我们可以将 createServer 中的匿名函数抽取出来,使得代码更为简洁:
var http = require("http");
function handler(request, response){
response.writeHead(200, {"Content-Type":"text/plain"});
response.write("httpserver");
response.end();
}
http.createServer(handler).listen(8080);
然后我们就可以放心的改造 handler 函数了,首先我们需要接受客户端发送的请求,这个 在 node.js 中很容易实现,为 request 添加两个监听器:对于数据到达的事件,node 会 触发”data”事件,当数据传输完成之后,会触发”end”事件。
function handler(request, response){
var data = "";
request.addListener("data", function(chunk){
data += chunk;
});
request.addListener("end", function(){
//invoke the real action handler
});
}
当 data 事件触发后,陆续的将接收到的数据拼接在 data 变量中,当 end 事件触发时,我们就可以将数据传递给真实的处理函数来完成了。 客户端请求的 URL 格式为:http://host:ip/path,我们可以通过 node.js 的 url 模块来解析这个 path,这样我们就得到的操作类型。
var url = require("url");
function handler(request, response){
var data = "";
var path = url.parse(request.url).pathname;
request.addListener("data", function(chunk){ data += chunk;
});
request.addListener("end", function(){
//RESUful
if(path == ""){
//do something
}else if(path == ""){
//do something else
}else{
//error
}
});
}
然后,在 end 事件触发时,我们可以根据请求名称来进行分发。但是这样的写法并不好,如果请求类型变得很大的时候,代码中会有很多的 if-else-if 判断,不但代码会很丑陋,而 且效率会降低。于是我们将定义一个请求名称和请求处理的映射表,当客户端的请求和映射表中的键匹配时,就调用这个键对应的值(这个值是一个函数):
var error = function(path, response){}
var echo = function(data, response){}
var upper = function(data, response){}
var lower = function(data, response){}
var map = {
"/echo" : echo,
"/upper" : upper,
"/lower" : lower,
"error" : error
};
我们将这个映射表放在 actions.js 模块中,并将 map 暴露出来,然后在 httpserver.js 中 require 它即可。这样在 end 事件的处理函数中,就变得非常简单了:
request.addListener("end", function(){
if(path in map){
map[path](data, response);
}else{
map["error"](path, response);
}
});
这时,我们的 handler 就有点名不副实了,它不负责处理具体的请求,仅仅是做分发,因 此更名为 dispatch,这样我们的 httpserver.js 就已经开发完成了:
var http = require("http");
var url = require("url");
var actions = require("./actions");
map = actions.map;
function dispatch(request, response){
var data = "";
var path = url.parse(request.url).pathname;
request.addListener("data", function(chunk){
data += chunk;
});
request.addListener("end", function(){
//RESUful
if(path in map){
map[path](data, response);
}else{
map["error"](path, response);
}
});
}
http.createServer(dispatch).listen(8080);
而 actions.js 中,我们的业务极为简单,只是对字符串进行大小写转换:
var error = function(path,
response){
response.writeHead(500, {"Content-Type" : "text/plain"});
response.write("no such action handler for "+path);
response.end();
}
var echo = function(data, response){
response.writeHead(200, {"Content-Type" : "text/plain"});
response.write(data);
response.end();
}
var upper = function(data, response){
response.writeHead(200, {"Content-Type" : "text/plain"});
response.write(data.toUpper());
response.end();
}
var lower = function(data, response){
response.writeHead(200, {"Content-Type" : "text/plain"}); response.write(data.toLower());
response.end();
}
var map = {
"echo" : echo,
"upper" : upper,
"lower" : lower,
"error" : error
};
exports.map = map;
运行服务器: node httpserver.js
然后使用 crul 进行测试:
$ curl -X POST http://10.111.43.117:8080/upper -d "hello, world" HELLO, WORLD
我们可以很容易的对这个小型的服务器进行扩展,以支持更多的 action。
{$ activeFileHint $}