Node.jsʵÏÖÒ»¸ö¼òµ¥µÄWeb MVC¿ò¼Ü - ÎÒÒª¿´Ã÷°×µÄµÚÒ»¸ömvc

¶øendʼþÔò»áÔÚ×îºó´¥·¢¡£ËùÒÔÎÒÃÇÔÚdataʼþÀïÃæ´¦Àí½ÓÊÕµ½µÄÊý¾Ý(ÀýÈçpost¹ýÀ´µÄform±íµ¥Êý¾Ý)£¬ÔÚendʼþÀïÃæÍ¨¹ýhandlerRequest º¯ÊýÀ´Í³Ò»´¦ÀíËùÓеÄÇëÇó²¢·Ö·¢¸øÏàÓ¦µÄcontroller action´¦Àí¡£ handlerRequestµÄ´úÂëÈçÏ£º

var route = require('./route'); var handlerRequest = function(req, res){ //ͨ¹ýrouteÀ´»ñÈ¡controllerºÍactionÐÅÏ¢ var actionInfo = route.getActionInfo(req.url, req.method); //Èç¹ûrouteÖÐÓÐÆ¥ÅäµÄaction£¬Ôò·Ö·¢¸ø¶ÔÓ¦µÄaction if(actionInfo.action){ //¼ÙÉècontroller¶¼·Åµ½µ±Ç°Ä¿Â¼µÄcontrollersĿ¼ÀïÃæ£¬»¹¼ÇµÃrequireÊÇÔõôËÑË÷moduleµÄô? var controller = require('./controllers/'+actionInfo.controller); // ./controllers/blog if(controller[actionInfo.action]){ var ct = new controllerContext(req, res); //¶¯Ì¬µ÷Ó㬶¯Ì¬ÓïÑÔ¾ÍÊÇ·½±ã°¡ //ͨ¹ýapply½«controllerµÄÉÏÏÂÎĶÔÏ󴫵ݸøaction controller[actionInfo.action].apply(ct,

actionInfo.args); }else{ handler500(req, res, 'Error: controller \actionInfo.action + '\Èç¹ûrouteûÓÐÆ¥Åäµ½£¬Ôòµ±×÷¾²Ì¬Îļþ´¦Àí staticFileServer(req, res); } }; ÕâÀïµ¼ÈëÀ´Ò»¸örouteÄ£¿é£¬route¸ù¾ÝÇëÇóµÄurlµÈÐÅϢȥ»ñÈ¡»ñÈ¡controllerºÍactionµÄÐÅÏ¢£¬Èç¹û»ñÈ¡µ½£¬Ôòͨ¹ý¶¯Ì¬µ÷Óõ÷ÓÃaction·½·¨£¬Èç¹ûûÓÐÆ¥ÅäµÄactionÐÅÏ¢£¬Ôò×÷Ϊ¾²Ì¬Îļþ´¦Àí¡£ ÏÂÃæÊÇrouteÄ£¿éµÄ´úÂ룺

var parseURL = require('url').parse; //¸ù¾ÝhttpÇëÇóµÄmethodÀ´·Ö±ð±£´æroute¹æÔò var routes = {get:[], post:[], head:[], put:[], delete:[]}; /** * ×¢²ároute¹æÔò * ʾÀý£º * route.map({ * method:'post', * url: /\\/blog\\/post\\/(\\d+)\\/?$/i, * controller: 'blog', * action: 'showBlogPost' * }) */ exports.map =

function(dict){ if(dict && dict.url && dict.controller){ var method = dict.method ? dict.method.toLowerCase() : 'get'; routes[method].push({ u: dict.url, //urlÆ¥ÅäÕýÔò c: dict.controller, a: dict.action || 'index' }); } }; exports.getActionInfo = function(url, method){ var r = {controller:null, action:null, args:null}, method = method ? method.toLowerCase() : 'get', // url: /blog/index?page=1 ,ÔòpathnameΪ: /blog/index pathname = parseURL(url).pathname; var m_routes = routes[method]; for(var i in m_routes){ //ÕýÔòÆ¥Åä r.args =

m_routes[i].u.exec(pathname); if(r.args){ r.controller = m_routes[i].c; r.action = m_routes[i].a; r.args.shift(); //µÚÒ»¸öֵΪƥÅäµ½µÄÕû¸öurl£¬È¥µô break; } } //Èç¹ûÆ¥Åäµ½route£¬r´ó¸ÅÊÇ {controller:'blog', action:'index', args:['1']} return r; };

map·½·¨ÓÃÓÚ×¢²á·ÓɹæÔò£¬ÎÒÃÇн¨Ò»¸öconfig.jsµÄÎļþ£¬À´ÅäÖÃroute¹æÔò£º

//config.js var route = require('./route'); route.map({ method:'get', url: /\\/blog\\/?$/i, controller: 'blog', action: 'index' });

Èç¹ûÇëÇóµÄurlÓÐÆ¥ÅäµÄroute¹æÔò£¬Ôò»á·µ»ØcontrollerºÍactionÐÅÏ¢¡£ÀýÈçÉÏÃæµÄrouteÅäÖ㬵±·ÃÎÊ /blog Õâ¸öurlµÄʱºò£¬Ôò»áµ÷Óà ./controllers/blog.js Ä£¿éÀïÃæµÄindexº¯Êý¡£ µ±µ÷ÓÃactionµÄʱºò£¬»á´«µÝcontrollerContext¸øacation£º

var ct = new controllerContext(req, res); //¶¯Ì¬µ÷Ó㬶¯Ì¬ÓïÑÔ¾ÍÊÇ·½±ã°¡ //ͨ¹ýapply½«controllerµÄÉÏÏÂÎĶÔÏ󴫵ݸøaction controller[actionInfo.action].apply(ct, actionInfo.args);

ÕâÀï»áͨ¹ýapply½«controllerContext×÷ΪactionµÄthis£¬²¢´«µÝargs×÷ΪactionµÄ²ÎÊýÀ´µ÷ÓÃaction¡£ ontrollerContext·â×°ÁËһЩaction»áÓõ½µÄ·½·¨£º

//controllerµÄÉÏÏÂÎĶÔÏó var controllerContext = function(req, res){ this.req = req; this.res = res; this.handler404 = handler404; this.handler500 = handler500; }; controllerContext.prototype.render = function(viewName, context){ viewEngine.render(this.req, this.res, viewName, context); };

controllerContext.prototype.renderJson = function(json){ viewEngine.renderJson(this.req, this.res, json); }; ÔÚactionÖд¦ÀíÍêÂß¼­»ñÈ¡»ñÈ¡µ½Óû§ÐèÒªµÄÊý¾Ýºó£¬¾ÍÒª³ÊÏÖ¸øÓû§¡£Õâ¾ÍÐèÒªviewEngineÀ´´¦ÀíÁË¡£ViewEngineµÄ´úÂëÈçÏ£º

var viewEngine = { render: function(req, res, viewName, context){ var filename = path.join(__dirname, 'views', viewName); try{ var output = Shotenjin.renderView(filename, context); }catch(err){ handler500(req, res, err); return; } res.writeHead(200, {'Content-Type': 'text/html'}); res.end(output); }, renderJson: function(res, json){ //TODO: } };

ÕâÀïviewEngineÖ÷Òª¸ºÔðÄ£°å½âÎö¡£nodeÓкܶàµÄ¿ÉÓõÄÄ£¿é£¬Ä£°å½âÎöÄ£¿éÒ²ÓÐÒ»´ó¶Ñ£¬²»¹ýÕâÀïÎÒÃÇÊÇÒª¡°Í桱£¬ËùÒÔÄ£°å½âÎöϵͳÎÒÃÇÕâÀïʹÓÃjstenjinÀ´ÉÔ×÷Ð޸ģº

//shotenjin.js Ôö¼ÓµÄ´úÂë //Ä£°å»º´æ£¬»º´æ½âÎöºóµÄÄ£°å Shotenjin.templateCatch = {}; //¶Áȡģ°åÄÚÈÝ //ÔÚÄ£°åÖÐÒýÓÃÄ£°åʹÓ㺠{# ../layout.html #} Shotenjin.getTemplateStr =

function(filename){ //console.log('get template:' + filename); var t = ''; //ÕâÀïʹÓõÄÊÇͬ²½¶ÁÈ¡

if(path.existsSync(filename)){ t = fs.readFileSync(filename, 'utf-8'); }else{ throw 'View: ' + filename + ' not exists'; } t = t.replace(/\\{#[\\s]*([\\.\\/\\w\\-]+)[\\s]*#\\}/ig, function(m, g1) { var fp = path.join(filename, g1.trim()) return Shotenjin.getTemplateStr(fp); }); return t; }; Shotenjin.renderView = function(viewPath, context) { var template = Shotenjin.templateCatch[viewPath]; if(!template){ var template_str = Shotenjin.getTemplateStr(viewPath); var template = new Shotenjin.Template(); template.convert(template_str); //Ìí¼Óµ½»º´æÖÐ

Shotenjin.templateCatch[viewPath] = template; } var output = template.render(context); return output; }; global.Shotenjin = Shotenjin;

Ôö¼ÓµÄ´úÂëÖ÷ÒªÊǶÁȡģ°åµÄÄÚÈÝ£¬²¢½âÎöÄ£°åÖÐÀàËÆ {# ../layout.html #} µÄ±êÇ©£¬µÝ¹é¶ÁÈ¡ËùÓеÄÄ£°åÄÚÈÝ£¬È»ºóµ÷ÓÃjstenjinµÄ·½·¨À´½âÎöÄ£°å¡£ ÕâÀï¶ÁÈ¡ÎļþÄÚÈÝʹÓõÄÊÇfs.readFileSync£¬ÕâÊÇͬ²½×èÈû¶ÁÈ¡ÎļþÄÚÈݵģ¬ºÍÎÒÃÇÆ½Ê±Ê¹ÓõĴó¶à±à³ÌÓïÑÔÒ»Ñù£¬¶øfs.readFileµÄ·Ç×èÈûÒì²½¶Á¡£ ÕâÀïµÄshotenjin.jsÔ­À´ÊǸø¿Í»§¶Ëwebä¯ÀÀÆ÷javascript½âÎöÄ£°åÓõģ¬ÏÖÔÚÄõ½node.jsÀ´Óã¬ÍêÈ«²»ÓÃÐ޸ľÍÕý³£¹¤×÷¡£Google V8ÕæÍþÎä¡£ ÏÖÔÚ»ù±¾µÄ¶«Î÷¶¼Íê³ÉÁË£¬µ«ÊǶÔÓÚ¾²Ì¬Îļþ£¬ÀýÈçjs¡¢cssµÈÎÒÃÇÐèÒªÒ»¸ö¾²Ì¬Îļþ·þÎñÆ÷£º

var staticFileServer = function(req, res, filePath){ if(!filePath){ filePath = path.join(__dirname, config.staticFileDir, url.parse(req.url).pathname); } path.exists(filePath, function(exists) { if(!exists)

{ handler404(req, res); return; } fs.readFile(filePath, \return; } var ext = path.extname(filePath); ext = ext ? ext.slice(1) : 'html'; res.writeHead(200, {'Content-Type': contentTypes[ext] || 'text/html'}); res.write(file, \\Ê¡ÂÔ }

¼òµ¥À´Ëµ¾ÍÊǶÁÈ¡ÎļþÄÚÈݲ¢Ð´Èëµ½responseÖзµ»Ø¸ø¿Í»§¶Ë¡£ ÏÖÔÚ¸ÃÓеͼÓÐÁË£¬ÎÒÃÇдһ¸öaction£º

// ./controllers/blog.js exports.index = function(){ this.render('blog/index.html', {msg:'Hello World'}); };

blog/index.htmlµÄÄÚÈÝΪ£º {# ../../header.html #}

n2Mvc Demo

#{msg}

{# ../../footer.html #}

½Ó×Å£¬¾ÍÊÇдһ¸ö½Å±¾À´Æô¶¯ÎÒÃǵÄn2MvcÁË£º

// run.js var n2MvcServer = require('./server'); n2MvcServer.runServer(); ok£¬ÔËÐÐÎÒÃÇµÄÆô¶¯½Å±¾£º ÔÚä¯ÀÀÆ÷·ÃÎÊ¿´¿´£º

àÅàÅ£¬Ò»ÇÐÕý³£¡£ ºÃ£¬½ÓÏÂÀ´ÎÒÃÇÔÙдһ¸ö»ñÈ¡ÐÂÀË΢²©×îÐÂ΢²©µÄÒ³Ãæ¡£Ê×ÏÈ£¬ÎÒÃÇÔÚconfig.jsÖÐÔö¼ÓÒ»¸örouteÅäÖãº

route.map({ method:'get', url: /\\/tweets\\/?$/i, controller: 'blog', action: 'tweets' }); È»ºó¿ªÊ¼Ð´ÎÒÃǵÄcnotroller action£º

var http = require('http'), events = require(\\function(blogType){ var _t = this; var listener = tweets_emitter.once(\{ _t.render('blog/tweets.html', {tweets: tweets}); }); get_tweets(); }; function get_tweets() { var request = tsina_client.request(\request.addListener(\{ body += data; }); response.addListener(\{ console.log('get tweets \\n'); tweets_emitter.emit(\

ÕâÀïʹÓÃhttp.createClientÀ´·¢ËÍÇëÇó»ñÈ¡ÐÂÀË΢²©µÄ×îÐÂ΢²©£¬È»ºó×¢²áÏàӦʼþµÄ¼àÌý¡£ÕâÀïÏêϸ˵ÏÂnodeµÄʼþϵͳ£ºEventEmitter¡£ EventEmitter¿ÉÒÔͨ¹ýrequire(¡®events¡¯). EventEmitterÀ´·ÃÎÊ£¬´´½¨Ò»¸ö EventEmitterµÄʵÀýemitterºó£¬¾Í¿ÉÒÔͨ¹ýÕâ¸öemitterÀ´×¢²á¡¢É¾³ý¡¢·¢³öʼþÁË¡£ ÀýÈçÉÏÃæµÄ´úÂëÖУ¬ÏÈ´´½¨À´Ò»¸öEventEmitterµÄʵÀý£º var tweets_emitter = new events.EventEmitter(); È»ºóÓÃonce×¢²áÒ»¸öÒ»´ÎÐÔµÄʼþ¼àÌý£º

var listener = tweets_emitter.once(\tweets}); });

once×¢²áµÄʼþÔÚʼþ±»´¥·¢Ò»´Îºó£¬¾Í»á×Ô¶¯ÒƳý¡£ ×îºó£¬Í¨¹ýemitÀ´·¢³öʼþ£º tweets_emitter.emit(\

ÕâÑù£¬Õû¸öʼþµÄÁ÷³Ì¶¼ÇåÎúÁË¡£ ÏÂÃæÐ´Ò»ÏÂÏÔʾtweetsµÄÄ£°å£º ÍòÊ´󼪣¬ÔËÐв¢·ÃÎÊ£º

¸½Ò»¸ö¼òµ¥µÄºÍDjangoµÄ¶Ô±È²âÊÔ

ÁªÏµ¿Í·þ£º779662525#qq.com(#Ìæ»»Îª@) ËÕICP±¸20003344ºÅ-4