nodejs實現簡易MVC


  相信大家對於nodejs應該不會陌生,如果真的比較陌生的請訪問:http://nodejs.org或者http://cnodejs.org/了解。

  這個簡易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的效果如下:

  

  

  使用用戶名登錄

  

  直接訪問/user/load

  

 

  簡易mvc就完成啦,如有什么錯誤和建議請給我留言,^_^,源代碼在此


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM