用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);
		})
	}
}
相關文章
相關標籤/搜索