之前的程式著重在伺服器流程的構成,還沒有考慮到如何提供使用者可用的api,所以再來加強一下。
目前的伺服器流程
使用者可以自訂的地方,主要在pre(pre dispatch)這個hook,但是通常除了在流程中對http.ServerRequest做預處理,很多時候還需要在伺服器中加入一些API來使用。
構想很簡單,就是設計好一個plugin模組的格式,然後利用一個use方法把plugin載入。例如如果要把cookie相關的功能移到plugin,可以這樣設計plugin模組(cookie.js):
module.exports = {
app: {
"cookieParser": function(cookie_str) {
var tmp1 = cookie_str.split(/[;,] */g),
tmp2,
i=0,
l=tmp1.length,
ret={};
for(;i<l;i++) {
tmp2 = tmp1[i].split('=');
ret[tmp2[0].trim().replace(/^[\'\"]+/g, '').replace(/[\'\"]+$/g, '')] = tmp2[1].trim().replace(/^[\'\"]+/g, '').replace(/[\'\"]+$/g, '');
}
return ret;
},
"setCookie": function() {
}
},
pre: [
function(request, response, next) {
if(request.headers.cookie) {
request.cookie = this.cookieParser(request.headers.cookie);
} else {
request.cookie = {};
}
next();
}
]
};
然後在伺服器加上一個use方法(evolve.js):
this.use = function(name) {
var obj = require('./plugins/'+name);//plugin集中放置在lib/plugins目錄中
var i = '';
for(i in obj) {
if(obj.hasOwnProperty(i)) {
switch(i) {
case 'pre':
case 'dispatch':
case 'post':
var j = 0, l = obj[i].length;
for(; j<l; j++) {
this.handle(i, obj[i][j]);
}
break;
case 'app':
var k = '';
for(k in obj.app) {
if(obj.app.hasOwnProperty(k)) {
switch(k) {
case 'handle':
case 'use':
case 'end':
case 'host':
case 'listen':
case 'close':
case 'listen':
break;
default:
if(typeof obj.app[k] === 'function') {
this[k] = obj.app[k];
}
break;
}
}
}
break;
}
}
}
return this;
};
這樣就可以把定義好的api:cookieParser以及setCookie加到伺服器的instance中,讓使用者可以在router中使用。
使用
在需要使用到cookie相關功能時,就可以(調整一下之前寫的整合測試:testCookie.js):
var Evolve = require('../lib/evolve');
var testCase = require('nodeunit').testCase;
module.exports = testCase({
"setUp": function(cb) {
this.http = require('http');
this.evolve = new Evolve({dirindex: ['index.html', 'index.htm', 'default.htm']})
.use('cookie')
.host('localhost:8443')
.get('/testcookie', function(request, response, cb) {
if(request.cookie) {
var str = ''+request.headers.cookie;
this.setCookie(...); // 還沒實作出來
cb(false, '/testcookie', {type:'text/plain', data: str}, true);
}else{
var str = "no cookie";
cb(false, '/testcookie', {type:'text/plain', data: str}, true)
}
})
.listen(8443, 'localhost');
cb();
},
"tearDown": function(cb) {
this.evolve.close();
cb();
},
"test if cookie received and appended to request": function(test) {
test.expect(2);
var cv = new Date().getTime();
var req = this.http.request({
"host": "localhost",
"port": 8443,
"path": "/testcookie",
"method": "GET",
"headers": {
"Cookie": "SID="+cv
}
}, function(response) {
var result = [];
response.on('data', function(data) {
result.push(data);
});
response.on('end', function() {
var total = 0;
var entity = '';
for(var i=0; i<result.length; i++) {
total += result[i].length;
entity += result[i].toString('ascii');
}
test.ok(entity.indexOf('SID')>-1);
test.ok(entity.indexOf(cv+"")>-1);
//可以檢查是否有set新的cookie
test.done();
});
});
req.end();
}
});
功能就更完整了,也更模組化。
就先到這裡告一段落吧,之後再慢慢把功能實作進去。