一种nodejs的MVC框架
mvc会针对请求进行分发,分发一般有controller(针对模块),action(针对模块中的方法),args(请求的参数)。
1.先对http请求的url进行设置,解析url中的各种参数:
//config.js var route = require('./route'); //针对ajax请求
route.map({ method:'get', url: /^\/category/mailCategory/i, controller: 'mailCategory', action: 'getMailCategory' });
//controller为空,针对页面,针对页面 route.map({ method: 'get', url: /^\/$/i, controller: '', action: 'list.html' });
server.js的入口:
exports.runServer = function(port){ port = port || 10088; /* function(req,res){}它将会自动加入到 'request' 事件的监听队列。 它是http.IncomingMessage的一个实例。 */ var server = http.createServer(function(req, res){ var _postData = ''; //on用于添加一个监听函数到一个特定的事件 req.on('data', function(chunk) { _postData += chunk; }) .on('end', function() { req.post = querystring.parse(_postData); handlerRequest(req, res); }); }).listen(port); //程序运行的入口 console.log('Server running at http://180.150.189.25:'+ 10088 +'/'); };
当接收完请求的URL之后,执行handlerRequest(req, res);
/** * 所有请求的统一入口 */ var handlerRequest = function(req, res){ //通过route来获取controller和action信息 var actionInfo = route.getActionInfo(req.url, req.method); //如果route中有匹配的action,则分发给对应的action if(actionInfo.action){ var controller = actionInfo.controller; //直接跳转页面 if(controller == ""){ viewEngine.forward(req, res, actionInfo.action); return; } //加载action if(controller[actionInfo.action]){ var ct = new controllerContext(req, res); //如果是post请求,从重新设置参数 if(req.method.toLowerCase() == "post"){ actionInfo.args = req.post; } //通过apply将controller的上下文对象传递给action,路径转化就在这里。 controller[actionInfo.action].call(ct, actionInfo.args); }else{ handler500(req, res, 'Error: controller "' + actionInfo.controller + '" without action "' + actionInfo.action + '"') } }else{ //如果route没有匹配到,则当作静态文件处理 staticFileServer(req, res); } };
viewEngine.forward代码如下:
var staticFileServer = function(req, res, filePath){ if(!filePath){ filePath = path.join(__dirname, config.staticFileDir, url.parse(req.url).pathname); } fs.exists(filePath, function(exists) { if(!exists) { //目录不存在的情况 handler404(req, res); return; } var raw = fs.createReadStream(filePath); var ext = path.extname(filePath); ext = ext ? ext.slice(1) : 'html'; var acceptEncoding = req.headers['accept-encoding']; var head = {'Content-Type': contentTypes[ext] || 'text/html'}; if (!acceptEncoding) { acceptEncoding = ''; } //根据请求头部信息,返回相应的数据 if(acceptEncoding.match(/\bgzip\b/)) { head["content-encoding"] = "gzip"; res.writeHead(200, head); raw.pipe(zlib.createGzip()).pipe(res); }else if (acceptEncoding.match(/\bdeflate\b/)) { head["content-encoding"] = "deflate"; res.writeHead(200, head); raw.pipe(zlib.createDeflate()).pipe(res); } else { res.writeHead(200, head); raw.pipe(res); } }); };
ct的上下文如下:
//controller的上下文对象 var controllerContext = function(req, res){ this.req = req; this.res = res; this.dbPool = dbPool; this.contextPath = __dirname; this.handler404 = handler404; this.handler500 = handler500; };
2.controller
通过上面的转发就可以把URL与module中的方法映射起来,我们写方法就可以按照模块来写了,例如mailCategory中的getMailCategory的方法如下:
在src模块中的action模块的MailCategory.js中:
/*MailCategory.js,提供两个action getMailList 和 getMailCategory */ exports.getMailList = function(args){ var context = this; var result = {errCode: 0, msg: 'success', categoryId: categoryId, data:[]}; //参数校验 var categoryId = args.categoryId; if(!categoryId){ emitter.emit("invalidCategoryId", result, context); return false; } /** * 获取邮件栏目信息 * @param isList 1 只是返回栏目列表 0 返回栏目及模块信息 * @returns {errCode:0, msg:'success', data:[{id:'', name: '', alias: ''},...]} * @returns {errCode:0, msg:'success', data: [{id:'', name:'', alias:'', modules:[{id:'', name:''}, ...]}]} */ exports.getMailCategory = function(args){ }
3.module
为controller层提供数据,直接与数据库连接,或者中间加一道缓存,例如redis。刚刚看到在映射的过程中,传递对象的成员
this.dbPool = dbPool;
它的定义如下:
dbManager = require('./src/utilities/DbManager'); //数据库链接池 var dbPool = dbManager.getPool();
DbManager.js封装了连接数据库,或者一个连接池的对象
//创建mysql数据库链接 var mysql = require('mysql'); //连接池 var dbConfig = { host: '180.150.189.25', port: '3306', user: 'iyy', password: 'iyy@2015', database: 'anthony', connectionLimit: 20 //连接池最大连接数 //waitForConnections: false } //从数据库获取一个链接 exports.getConnection = function(){ return mysql.createConnection(dbConfig); } exports.getPool = function(){ var pool = mysql.createPool(dbConfig); return pool; }
这样就可以在controller模块中直接操作数据库,如下:
context.dbPool.getConnection(function(err,connection){ if(err){ emitter.emit("connectFailed", result, context); return false; } //可以进一步拆分到module中。 connection.query("delete from MailModule where id="+intDeleteId,function(err,res){ if(err) { emitter.emit("queryFailed", result, context); return false; } /* emitter.on("success", function(result, context){ result.errCode = 0; result.msg = "success"; var str='updateuserinfo_callback({"ret":2,"errmsg":"asdas"})'; context.renderJson(str); }); */ emitter.emit("success", result, context); }); //释放链接 connection.end(); });