nodejs实现简易MVC

  相信大家对于nodejs应该不会陌生,如果真的比较陌生的请访问:http://nodejs.org或者http://cnodejs.org/了解。

  这个简易MVC的结构如下图:

    nodejs实现简易MVC

  首先需要一个http服务来监听来自客户端的请求,大致代码如下:

var m_http = require('http');

var m_querystring = require('querystring');



var m_requestHandler = require('./requestHandler');



exports.run = function (port) {

    port || (port = 80);



    m_http.createServer(function (req, res) {

        req.setEncoding('utf8');



        var postData = [];



        req.on('data', function (chunk) {

            postData.push(chunk);

        }).on('end', function () {

            req.post = m_querystring.parse(postData.join(''));



            m_requestHandler.handle(req, res);

        });



    }).listen(port);



    console.log('服务器启动!');

};

  以上req.on('data', ...)内,使用的是一个postData的数组来保存请求的数据,但是当数据量大的时候,会出现一些问题,这个我们暂时不管,博友么可以自己去完善,^_^

  接收了请求之后,需要通过请求的url和method去查找与之相匹配的Contoller,并调用Controller相应的Action,大致代码如下:

var m_route = require('./route');

var m_controllerBase = require('./controllerBase');

var m_invalidHandler = require('./invalidHandler');



var m_getRequestArgs = { ... };



exports.handle = function (req, res) {

    var method = req.method ? req.method.toLowerCase() : 'get';

    var route = m_route.find(req.url, method);



	var controller = require(m_util.format('./controllers/%s', route.controller));

	if (controller[route.action]) {

	    try {

	        controller[route.action].call(

	            new m_controllerBase(req, res),

	            m_getRequestArgs.hasOwnProperty(method) ? m_getRequestArgs[method](req) : {});

	    }

	    catch (e) {

	        m_invalidHandler.handle500(req, res);

	    }

	}

	else {

	    m_invalidHandler.handle404(req, res);

	}

};

  在这里引用了另外3个模块,route.js会根据请求的url和method去获取对应的controller和action信息,代码如下:

//缓存映射信息

var m_cache = {};



exports.addMap = function (map) {

    if (!(map && map.rule && map.controller))

        return;



    var method = (map.method || 'get').toLowerCase();

    m_cache[method] || (m_cache[method] = []);



    m_cache[method].push({

        rule: map.rule,

        controller: map.controller,

        action: map.action || 'index'

    });

};



exports.find = function (url, method) {

    var route = { controller: null, action: null };



    var routes;

    if (!(m_cache.hasOwnProperty(method) && (routes = m_cache[method]).length))

        return route;



    for (var i = 0, r; r = routes[i]; i++) {

        if (r.rule.test(url)) {

            route.controller = r.controller;

            route.action = r.action;

            break;

        }

    }

    return route;

};

  而controllerBase相当于是所有Controller的基类,会提供诸如缓存,输出html、json、script等方法,代码如下:

function controllerBase(req, res) {

    this.req = req;

    this.res = res;

};

//添加缓存

controllerBase.prototype.addCache = function (key, value) {

    m_cache[key] = value;

};

//添加缓存

controllerBase.prototype.getCache = function (key) {

    return m_cache[key];

};

//返回html

controllerBase.prototype.html = function (viewName) {

	//coding

};

//根据模板生成的,可以使用其他模板来实现

controllerBase.prototype.template = function (viewName, obj) {

	//coding

};

//返回json

controllerBase.prototype.json = function (obj) {

	//coding

};



module.exports = controllerBase;

  但是在处理这些操作的事情,可能会出现错误,因此就需要一个invalidHandler来处理一些原因导致请求无法返回的情况,代码大致如下:

//简单实现了404和500 对于其他http状态码大家可以根据情况实现



exports.handle404 = function (req, res) {

    res.writeHead(404, {});

    res.end();

};



exports.handle500 = function (req, res) {

    res.writeHead(500, {});

    res.end();

};

  当我做到这里的时候,我突然发现并不是所有的请求都会触发Controller和Action,有的仅仅是请求一些如图片、js、css等方面的静态文件,所以我们在处理请求的时候,应该增加一个处理静态文件的,代码如下:

exports.handle = function (req, res) {

    var url = m_parseURL(req.url);

	//当请求的文件是网站图标时,不映射到静态文件夹下

    var filePath = url.pathname == m_config.FAVICON ? m_path.join(__dirname, url.pathname) :

        m_path.join(__dirname, m_config.STATIC_FILE_DIR, url.pathname);



    m_fs.exists(filePath, function (exists) {

        if (!exists) {

            m_invalidHandler.handle404(req, res);

            return;

        }



        m_fs.readFile(filePath, FILE_ENCODING, function (err, file) {

            if (err) {

                m_invalidHandler.handle500(req, res, err);

                return;

            }



            var ext;

            ext = (ext = m_path.extname(filePath)) ? ext.slice(1) : 'html';

            res.writeHead(200, { 'Content-Type': m_config.CONTENT_TYPE[ext] || m_config.CONTENT_TYPE.html });

            res.write(file, FILE_ENCODING);

            res.end();

        });

    });

};

  写到这里已经将简易的MVC完成了,最后再增加几个功能来跑起来看看吧,代码如下:

//base.js

exports.index = function () {

    this.view('index.html');

};



exports.login = function (data) {

    this.addCache('user', data.name);

    this.json({ success: true });

};





//user.js

exports.load = function () {

    var msg = this.getCache('user') || '未登录';

    this.template('main.html', { msg: msg });

};



//index.html

<div>用户名:<input id="txtName" type="text" value="admin" /></div>

<input id="btn" type="button" value="登录" />



//main.html

欢迎你来到建议MVC,{#msg}!

  以上增加了controllers和对应的action以及html页面,运行起来发现没效果,检查了代码原来是还没有配置对应route规则,于是我们再添加一些规则,代码如下:

m_route.addMap({

    rule: /^\/$/,

    controller: 'base'

});

m_route.addMap({

    method: 'post',

    rule: /^\/login/,

    controller: 'base',

    action: 'login'

});

m_route.addMap({

    rule: /^\/user\/load$/,

    controller: 'user',

    action: 'load'

});

  整个mvc的效果如下:

  

  nodejs实现简易MVC

  使用用户名登录

  nodejs实现简易MVC

  直接访问/user/load

  nodejs实现简易MVC

 

  简易mvc就完成啦,如有什么错误和建议请给我留言,^_^,源代码在此

你可能感兴趣的:(nodejs)