用Node.js 写web框架(四)

不得不说,基本上咱们还没到写框架的日子,前面这些,彻底都是在写一个web服务器(并且还没写完)。 html

今天的工做主要是整理。 node

首先来谈谈Node.js中模块的概念。基本上咱们之前的工做就是:写一个js文件,而后根据相对路径来引用它。这里出现一个问题,就是相对路径实际上并不稳定,并且咱们后期可能把全部框架级别的内容单独放置在一个文件夹中,这时候一样会改变相对路径,因此须要引入模块来实现require时候的稳定。 web

Node.js中从路径Y中require(X)的流程为: 数据库

1. 若是X是核心模块(如http、fs等),返回核心模块
2. 若是X以'./'或者'../'或者'/'开始,首先尝试以文件方式加载,失败则尝试以文件夹方式加载
3. 若是2失败,则尝试在node_modules目录下查找该模块 编程

更详细内容能够看这个:
Node.js API json

这里咱们使用第三种,就是把全部框架级别的文件单独放在node_modules下的一个文件夹下,并根据CommonJS关于Package的规范来从新整理文件夹结构。 api

首先是文件夹结构:
顶层文件夹中,包含一个文件:package.json,用于描述包信息
二进制文件放在/bin下
Javascript文件放在/lib下
文档放在/doc下
测试文件放在/test下 服务器

原文:http://wiki.commonjs.org/wiki/Packages/1.0#Package_Directory_Layout session

整理之后,简单的写一下package.json 框架

{ 
	"name" : "restjs",
	"main" : "./lib/main.js",
	"maintainers" : [
		{
			"name" : "Jeky Cui",
			"email" : "Jeky.Cui@gmail.com"
		}
	]
}
好,模块文件整理部分到此结束。


下面开始分离框架部分。这里遇到的问题依然是路径,实在没办法了,只好使用fs.realpath来解决:

var root = fs.realpathSync('./') + '/';


这里须要解释一下,我不是不喜欢异步编程,只是以为服务器启动期间彻底能够用同步来写,简单明了。

另外一件事情就是把配置独立出来,若是用户须要修改配置,那么就在构造Dispatcher时修改就能够了。而且,我在配置里添加了一项叫作welcomeFile,若是配置了welcomeFile,那么当访问网站首页时,会直接跳转至welcomeFile。


module.exports = {
	staticPath : 'static',
	maxAge : 1000 * 60 * 60 * 24 * 7,
	welcomeFile : 'index.html',
	devMode : true,
	modulePath : 'modules',
	indexName : 'index',
	viewFilename : 'views.js'
}


好,到此为止,彻底分离了服务器&框架部分和业务逻辑部分。实际上到目前为止,已经写完了一个基本可用的静态服务器。


下面开始作动态部分。主要内容有:
1. 请求的封装
2. session的处理
3. 模板渲染
4. 数据库

今天完成请求封装部分。

实际上对于GET请求的封装,Node.js已经帮咱们完成了:
http://nodejs.org/api/querystring.html#querystring_querystring_parse_str_sep_eq_options

function wrapParam(req){
	if(req.method == 'GET'){
		var queryStr = url.parse(req.url).query;
		if(!queryStr){
			return {};
		}
		return qs.parse(queryStr);
	}else if(req.method == 'POST'){
		// TODO
	}
}
下面只须要对于POST进行解析并封装就能够了。POST请求会触发request的'data'时间,只要监听这个事件就能够了。这里先暂时忽略掉文件上传这回事。
function wrapParam(req){
	if(req.method == 'GET'){
		var queryStr = url.parse(req.url).query;
		if(!queryStr){
			return {};
		}
		return qs.parse(queryStr);
	}else if(req.method == 'POST'){
		req.on('data', function(chunk){
			result = qs.parse(chunk.toString());
		})
	}
}
好是挺好,可是异步编程是没法直接把这个result返回的,因此还须要引入回调函数。
function wrapParam(req, callback){
	if(req.method == 'GET'){
		var queryStr = url.parse(req.url).query;
		if(!queryStr){
			callback({});
		}
		callback(qs.parse(queryStr));
	}else if(req.method == 'POST'){
		req.on('data', function(chunk){
			callback(qs.parse(chunk.toString()));
		})
	}
}
好,到目前为止都很顺利。明天起来完成文件上传的部分。


P.S. 谢谢 @mingshun 的指导,单纯检测data事件会致使POST数据不完整,须要在end事件时拼接POST数据,代码修改成:

function wrapParam(req, callback){
	if(req.method == 'GET'){
		var queryStr = url.parse(req.url).query;
		if(!queryStr){
			callback({});
		}
		callback(qs.parse(queryStr));
	}else if(req.method == 'POST'){
		var chunks = [];
		var length = 0;
		req.on('data', function(chunk){
			chunks.push(chunk);
			length += chunk.length;
		})
		req.on('end', function(){
			var buffer = undefined;
			if(chunks.length == 1){
				buffer = chunks[0];
			}else{
				buffer = new Buffer(length);
			}
			var len = 0;
			$(chunks).each(function(chunk){
				chunk.copy(buffer, len);
				len += chunk.length;
			});
			var param = qs.parse(buffer.toString());
			callback(param);
		})
	}
}
相关文章
相关标签/搜索