Node.js教程 模仿ASP.Net MVC的node.js MVC功能实现
小作 2018-02-17 来源 :网络 阅读 989 评论 0

摘要:本文提供了一个模仿ASP.Net MVC的node.js MVC框架的Node.js教程,基本实现了ASP.Net MVC的Model(模型)、View(视图)和Controller(控制器)的这三项基本功能,并且基于这个MVC框架的编码方式、程序结构和文件组织,同ASP.Net  MVC基本相同。

本文提供了一个模仿ASP.Net MVC的node.js MVC框架的Node.js教程,基本实现了ASP.Net MVC的Model(模型)、View(视图)和Controller(控制器)的这三项基本功能,并且基于这个MVC框架的编码方式、程序结构和文件组织,同ASP.Net  MVC基本相同。

 

一:为什么要做这个东西

  首先声明,我对ASP.Net MVC研究不深,对node.js更是仅知皮毛。

  多年来我主要做ASP.Net WebForm的开发,近来偶然接触了MVC,令人耳目一新。感觉MVC同WebForm有很大的区别,源代码更简洁、目标代码更干净、系统运行速度更快。虽然还没有找到类似于WebForm的可视化开发的方法,不过对我这种已经习惯手写代码的程序员而言,却没什么障碍。

  近来又接触了node.js,这真是一个神奇的东西:JavaScript做服务器端开发、代码及其简洁、(据说)速度也奇快。不过......不过对我这种已经习惯了ASP.Net开发方式的程序员,node.js仅提供基础功能支持,这对我们却是一种折磨。

  灵机一动,或者说,是为了以后的懒惰做准备,决定写一个简单的MVC框架。基本原则是:其使用方法同ASP.Net MVC基本相同(为了让我们少耗费一些脑细胞)。

  这个MVC框架我已经基本写完(草稿),本文就仅对已有的实现做简要说明,中间的挫折统统忽略吧。

  另外,这个MVC框架用到了一些第三方组件,这里提供其官方地址,若需要下载或对其有疑问,直接导航过去看:

underscore:一个实用的JavaScript工具库,提供很多对集合、数组、对象的快捷操作方法,地址://documentcloud.github.com/underscore/

underscore.string:另一个实用的JavaScript工具库,提供对字符串的快捷操作方法(实际上同underscore没有任何关系),地址:https://github.com/epeli/underscore.string

ejs:一个强大的JavaScript模板支持库,地址:https://github.com/visionmedia/ejs

  先具体说一下导致我产生这个想法的具体原因。

  为了学习node.js,从官方网站(//www.nodejs.org/)下载程序包,执行安装。(安装完成后,可执行文件node.exe放在C:\Program Files\nodejs中,如果不习惯的话,可以把整个文件夹拷贝到其他的位置。)

  首先,从Hello Word开始。在nodejs文件夹中新建一个文本文件,起名为index.js,用任意文本编辑器编辑: 

1 var http = require('http');

2 http.createServer(function (req, res) {

3     res.end('Hello World\n');

4 }).listen(81, "127.0.0.1");

5 console.log('Server running at //127.0.0.1:81/');

  然后在Dos窗口执行: node index.js。用浏览器打开://127.0.0.1:81,马上就能看到文本:Hello World。

  是不是很简单?不过怨念也就在这里了:我们做传统的Web开发,每个请求需要建立不同的文件,并使用不同的http地址来请求;而node.js,所有的请求都指向到这个根文件夹的index.js,这就需要我们经常性的在这个文件中添加代码,即使是使用伟大的express也不例外。这是express的一个样例,一般起名为app.js: 


 1 var express = require('express ');
 2 
 3 var app = express.createServer();
 4 
 5 app.get('/', function(req, res){
 6   res.send('root index');
 7 });
 8 
 9 app.get('/user/', function(req, res){
10   res.send('user's index');
11 });
12 
13 app.listen(82);
14 console.log('listening on port 82');

  当然,我不会去说express不好(因为这确实是个强大的组件),只是我不习惯;可能express有很好的方法,我不知道而已。

  于是我决定用node.js做一个类似于ASP.Net MVC的框架,以方便我以后继续学习使用node.js,或许也可以帮助到其他初学node.js的同学们。


二:这个东西应该是什么样子的

  既然是计划模仿ASP.Net MVC,那么就要尽量模仿的像一点。

  为什么我要模仿ASP.Net MVC,而不去模仿ASP.Net WebForm: 因为MVC够简单,而WebForm太复杂了,尤其是那一大堆服务器端控件,除非一个传奇级的任务,可不敢想象怎么去做。

  首先是ASP.Net MVC的目录结构。这个图片就是一个经典的ASP.Net MVC站点的目录结构(VS自动创建的,做ASP.Net MVC的同学应该非常熟悉) :

Node.js教程 模仿ASP.Net MVC的node.js MVC功能实现

  在这个图片中,有几个东西是我们需要关注的:

Content文件夹:用于放置静态文件,包括images、styles等等

Controllers文件夹:用于放置控制器程序文件

Models文件夹:用于放置数据模型文件

Views文件夹:用于放置视图文件(就是页面或控件模板)

  上述的四个文件夹是我们需要模仿的,而App_Start、Global.asax、Web.config等等的,或许到了后期会发现也需要做,现在就先算了。至于Scripts文件夹的内容,在我看来应该放到Content\Scripts子文件夹中 。

  作为适应这个框架工作方式的结果,或者说是我们的node.js MVC的目的:

全部静态文件放入Content文件夹,并可以通过HTTP地址直接访问,而不用增加任何程序代码

全部控制器文件放入Controllers文件夹,一个Controller文件包含有多个Action,每个Action的访问方式为://.../<controller>/<action>/...

全部视图文件放入Views文件夹,并且每个Controller在这里对应有一个同名的子文件夹

ASP.Net MVC的全部视图文件放入Models文件夹,不过node.js是基于javascript动态语言的,模型定义似乎并不重要,这里就忽略了

  为实现上述目的,我们就先建立一个基本的目录结构,之后的工作都围绕这个目录结构:

Node.js教程 模仿ASP.Net MVC的node.js MVC功能实现

  在这个框架中:

index.js文件永远不用修改

contents文件夹一般会包含images、script、styles等子文件夹,任何文件放进去就可以用,并且可以包含任意规格的子文件夹

controllers文件夹包含全部的服务器端javascript文件

views文件夹包含全部的服务器端模板文件,且每一个controler在此文件夹中对应一个同名的子文件夹


三:这个框架都实现了什么

  这个框架的程序基本程序逻辑如下图所示:

  Node.js教程 模仿ASP.Net MVC的node.js MVC功能实现

  根据以上逻辑图可以看出,此框架只有三个简单的分支:

静态请求直接输出

动态请求由控制器处理

若需要输出页面则调用模板引擎处理

  这里先说明一下:在此框架中,任何有文件后缀的请求均被认为是静态请求。

  为实现此框架编写了一系列的程序文件,以实现以下功能:

请求入口:即index.js文件,实现请求分发和简单的安全管理

静态页处理:读取contents文件夹中的静态文件

动态请求处理:处理动态请求并返回动态结果

Session支持:提供Web应用中的用户级缓存支持(等同于ASP.Net中的Session)

Cache支持:提供Web应用中的全局缓存支持(等同于ASP.Net中的Cache或Application)

模板处理:处理包含Html页面或板块的文件,并支持嵌入式开发

嵌入式代码支持:为嵌入式开发提供扩展支持,包括ASP.Net MVC中的Html语法

路由:用于支持除了“/<controller>/<action>/...”之外的其他浏览器请求(暂未实现)

  全部文件夹和文件结构如下表所示:

index.js:请求入口
- contents:静态内容文件夹
  - css
  - images
  - scripts
- controllers:动态内容程序文件夹
- views:动态内容模板文件夹
- node_modules:node.js类库文件夹
  - ejs:第三方模板处理组件
  - mvc:我们的MVC框架文件夹
    base.js:进行动态请求初始化操作,例如准备session等
    cache.js:支持全局缓存
    html.js:支持模板处理和嵌入式开发
    index.js:无功能,仅用于nodejs
    mvc.js:请求分发和处理
    routes.js:请求路由
    session.js:支持用户缓存
    utils.js:部分常用工具函数
  - underscore:第三方类库
  - underscore.string:第三方类库

四:MVC框架实现代码

  注意:我假定阅读本文的同学:

对javascript比较熟悉(不能是生手,否则可能看不懂这些代码)

对node.js已经有了初步的了解,比如require、module.exports等的用法

1. 请求入口(index.js)

   作为一个标准的node.js程序,一个根文件夹下的index.js是必须的。这个文件仅包含如下代码: 


1 var http = require('http');2 var mvc = require('mvc');3 4 http.createServer(function (req, res) {5     new mvc.startRequest(req, res);6 }).listen(88, "127.0.0.1");7 8 console.log('Server running at //127.0.0.1:88/');

   在这个文件中,第6行的listen方法的参数(及第8行的日志)需要根据实际情况进行修改,其他的可以总是保持不动。当然若实现了类似ASP.Net的web.config支持之后,这个index.js文件就再也不用修改了。

  在index.js文件中仅做了一件事,即调用mvc.startRequest方法以开始一个请求处理。其他的代码同node.js中的hello world样例程序没啥区别。

2. 分发与请求处理 (mvc.js)

  mvc.js程序是此框架的核心,主要实现了请求分发和处理入口。

  首先,采用node.js推荐的方法引入外部程序:

1 var url = require('url');2 var path = require('path');3 var fs = require('fs');4 var querystring = require('querystring');5 var _ = require('underscore');6 var _s = require('underscore.string');7 var utils = require('./utils');8 var routes = require('./routes');9 var base = require('./base');

 

  其中,1-4是node.js系统类库,5-6是第三方类库,7-9是我们的框架中的将被用到的类库(后面我们在详细介绍)。

  然后我们看看在index.js用到的mvc.startRequest方法,在这个方法中我们简单地根据有无文件后缀来区分是否是动态请求。

 1 module.exports.startRequest = function(req, res) { 2     //在处理请求前先在控制台打印日志 3     if (req.url!='/favicon.ico') { //favicon.ico文件一般情况下是浏览器自动请求的,对我们没用 4         console.log(_s.sprintf('Start request: %s\t%s', utils.dateString(new Date()), req.url)); 5     } 6     //初始化一个请求处理引擎实例 7     var handler = new RequestHandler(req, res) 8     var u = url.parse(req.url); 9     var ext = path.extname(u.pathname);10     if (ext) { //如果请求的文件有后缀则认为是静态请求11         handler.responseContent(u.pathname);12     }13     else { //否则是动态请求14         handler.handleDynamicRequest();15     }16 }


 

  注:为方便起见,代码说明我尽量都卸载代码注释中。

  上述代码中我们用到了一个RequestHandler类,用于处理请求。以下是这个类的定义:

1 function RequestHandler(req, res) {2     this.request = req;3     this.response = res;4 }

  我们使用RequestHander.responseContent方法来处理任何静态请求,即存储在/contents/文件夹中的文件。思路是:找到文件、读取文件、输出文件。


 1 RequestHandler.prototype.responseContent = function(filename) { 2     if (!filename) { //如果参数缺失,则直接提示错误(不过这种情况应该不会出现) 3         this.responseNoFound(); 4     } 5     else { 6         filename = _s.trim(filename,' \/\\');  7  8         var self = this; 9         var filepath = path.resolve('./contents/', filename);10         fs.readFile(filepath, function (err, data) {11             if (err) { //如果文件不存在或无法访问,则提示错误12                 self.responseNoFound();13             }14             else { //否则输出文件内容到客户端15                 self.response.write(data);16             }17             self.response.end(); //输出完成之后必须调用response.end方法18         });19     }20 }

 

  我们使用RequestHandler.handleDynamicRequest方法来处理动态请求。思路是:获取请求参数,调用控制器来处理请求。


 1 RequestHandler.prototype.handleDynamicRequest=function() { 2     var params = null; 3     var self = this; 4     if (self.request.method == 'POST') { //如果是post方式的请求,则需要异步读取请求参数 5         var buf = []; 6         self.request.addListener('data', function(chunk){  7             buf.push(chunk); 8         })   9         .addListener('end', function(){10             params = querystring.parse(buf.join('')); 11             self.handleController(params);12         })  13     }14     else { //如果不是post方式(是get方式)的请求,则直接使用url参数15         params = self.request.url.query;16         if (_.isString(params)) 17             params = querystring.parse(params);18             19         self.handleController(params);20     }21 }

 

  上述代码中的RequestHandler.handleController方法,用于装载控制器,并执行action:

 1 RequestHandler.prototype.handleController=function(params) { 2     var controllerInfo = routes.parseControllerInfo(this.request); //分析请求url,拆分出controller,action,parameters 3      4     if (controllerInfo 5         && controllerInfo.controllerName && controllerInfo.methodName 6         && !_s.startsWith(controllerInfo.controllerName, '_') 7         && !_s.startsWith(controllerInfo.methodName, '_')) { 8  9         var instance = this.createControllerInstance(controllerInfo.controllerName); //装载控制器10 11         if (instance12             && _.has(instance, controllerInfo.methodName) 13             && _.isFunction(instance[controllerInfo.methodName])) { //若存在action定义,则执行该action14             try { //注意:这里必须加try...cache,以防止action发生错误导致我们的网站崩溃15                 var res = instance[controllerInfo.methodName](controllerInfo.parameters, params);16                 this.response.end(res + "");17             }18             catch (err) {19                 console.log(_s.sprintf("Execute a controller method failed: %s", err));20                 utils.responseError(this.response, 500);21             }22         }23         else {24             this.responseNoFound();25         }26     }27     else {28         console.log(_s.sprintf('parse controller failed: %s', _.keys(controllerInfo)));29         console.log(_s.sprintf('%s', _.values(controllerInfo)));30         this.responseNoFound();31     }32 }


 

  装载控制器的RequestHandler.createControllerInstance方法的定义是:


1 RequestHandler.prototype.createControllerInstance = function(controllerName) { 2     var instance = null; 3  4     try { 5         var controller = require(path.resolve('./controllers/', controllerName)); // 使用require方法装载控制器 6          7         if (controller && _.isFunction(controller)) { 8             instance = new controller(this.request, this.response); 9         }10     }11     catch (err) {12         utils.responseError(this.response, 500);13     }14 15     return instance;16 }

   在RequestHandler中还定义了两个额外的方法:


 1 //初始化页面基类 2 module.exports.controller = function(req, res) { 3     return new base(req, res); 4 } 5  6  7 //当文件不存在时记录日志并提示错误 8 RequestHandler.prototype.responseNoFound = function() { 9     if (this.request.url!='/favicon.ico') {10         console.log(_s.sprintf('No found url: %s', this.request.url));11     }12     utils.responseError(this.response, 404);13 }

  

3. 动态请求初始化(base.js)

  在请求分发处理成功之后,我们就需要开始实际的业务处理,包括session、页面、模板等等。这些功能我们集中定义在base.js中:


1 var Cache = require('./cache'); 2 var Session = require('./session'); 3 var Html = require('./html'); 4 var routes = require('./routes'); 5  6 var BaseController = module.exports = function (req, res) { 7     this.request = req; 8     this.response = res; 9     10     this.cache = Cache; //全局缓存11     this.session = Session.current(req, res); //用户缓存12     this.html = new Html(this); //视图处理及嵌入式开发引擎13     14     this.controllerInfo = routes.parseControllerInfo(req);15 }16 17 //处理一个视图(模板),并返回结果内容18 BaseController.prototype.renderView = function(options) {19     return this.html.renderView(options);20 }21 22 //处理一个视图(模板),直接输出到客户端23 BaseController.prototype.responseView = function(options) {24     this.response.end(this.renderView(options));25 }


  

 4. 用户缓存处理 (session.js)

  Session是Web应用程序中一个非常重要的东西。对node.js来说,Session处理其实就是一个全局的数据集合的处理:


 1 var _ = require('underscore'); 2 var _s = require('underscore.string'); 3  4 //全部Session的存储空间 5 var SessionPool = {}; 6  7 //全局的Session过期时间 8 Session.prototype.expireTime = 20; //缺省20分钟 9 10 //新建一个SessionID11 function newSessionID() {12     var buff = [];13     var chars = '0123456789abcdefghijklmnopqrstuvwxyz';14     for (var i=0; i<32; i++) {15         buff.push(chars[Math.floor(Math.random() * 36)]);16     }17     return buff.join('');18 }19 20 //一个Session的定义21 var Session = function() {22     var self = this;23     self.sessionId = newSessionID();24     self.sessionItems = {};25     self._expireTime = Session.prototype.expireTime; 26     self._expireHandle = setInterval(function () { //每一分钟检查一次是否过期27         self._expireTime -= 1;28         if (self._expireTime <= 0) {29             clearInterval(self._expireHandle);30             self.dispose(); //过期则自动清除31         }32     }, 60000);33 }34 35 //清理一个Session36 Session.prototype.dispose = function() {37     if (this._expireHandle) {38         clearInterval(this._expireHandle);39         this._expireHandle = null;40     }41     42     for (var key in this.sessionItems) {43         delete this.sessionItems[key];44     }45     this.sessionItems = null;46     47     delete SessionPool[this.sessionId];48 }

  然后我们定义一些常用的Session操作方法:

 1 //判断Session项是否存在 2 Session.prototype.has = function(key) { 3     return _.has(this.sessionItems, key); 4 }; 5  6 //添加或更新一个Session项 7 Session.prototype.set = function(key, value) { 8     if (key) { 9         this.refresh();10         11         if (this.has(key) && this.sessionItems[key] == value) {12             return;13         }14 15         this.sessionItems[key] = value;16     }17 }18 19 //获取一个Session项20 Session.prototype.get = function(key) {21     this.refresh();22 23     if (key && this.has(key)) {24         return this.sessionItems[key];25     }26     27     return null;28 }29 30 //删除一个Session项31 Session.prototype.remove = function(key) {32     this.refresh();33     34     if (key && this.has(key)) {35         delete this.sessionItems[key];36     }37 }38 39 //刷新过期时间,40 Session.prototype.refresh = function() {41     this._expireTime = Session.prototype.expireTime; 42 }4344 module.exports = Session;


  用户Session应该能被自动创建和恢复,这里我们使用大部分浏览器都支持的cookie(如果你的浏览器不支持cookie那么就需要另想它法了)。

 1 module.exports.current = function(req, res) { 2     //从Request的header中存储的cookie中查找当前正在使用的SessionID 3     var headers = req.headers; 4     var sessionId = null, session = null; 5     var cookie = null; 6     if (_.has(headers, 'cookie')) { 7         cookie = headers['cookie']; 8         if (_s.startsWith(cookie, 'SessionID=')) { //此处可能不是很严谨,需要优化 9             sessionId = cookie.substr(10, 36);10         }11     }12 13     //激活Session池中的一个session14     if (sessionId && _.has(SessionPool, sessionId)) {15         session = SessionPool[sessionId];16     }17 18     //如果是新session,则自动创建一个,并写回cookie中19     if (!session) {20         session = new Session();21         SessionPool[session.sessionId] = session;22 23         res.setHeader('set-cookie', 'SessionID=' + session.sessionId); 24     }25     26     return session;27 }


 

   这个Session.current方法将在BaseController.js第11行被自动调用,然后在任何动态请求中就都可以所以用session了。 

 

5. 系统缓存处理 (cache.js)

  Cache同样是Web应用开发中的一个很重要的东西。它对应于ASP.Net中的Application集合,以及System.Web.Caching.Cache集合(ASP.Net中这两个东西可都可以实现全局缓存)。对node.js来说,Cache处理同Session差不多同样就是一个全局的数据集合的处理,不过它比Session要简单得多。


1 var _ = require('underscore'); 2  3 var CachePool = {}; 4  5 var Cache = function() {}; 6  7 //判断Cache是否存在 8 Cache.prototype.has = function(key) 9 {10     return _.has(CachePool, key);11 };12 13 //添加或更新一个Cache项14 Cache.prototype.set = function(key, value)15 {16     if (key) {17         CachePool[key] = value;18     }19 }20 21 //获取一个Cache项22 Cache.prototype.get = function(key)23 {24     if (key && Cache.prototype.has(key)) {25         return CachePool[key];26     }27     28     return null;29 }30 31 //移除一个Cache项32 Cache.prototype.remove = function(key) {33     if (Cache.prototype.has(key)) {34         delete CachePool[key];35     }36 }37 38 //建立一个全局的Cache容器39 module.exports = new Cache();


  注意:上述代码的第39行直接建立了一个类实例,即保证整个应用中只有一个Cache实例,以保证全局的唯一性。

  这个Cache将在BaseController.js第10行被自动调用,然后在任何动态请求中就都可以所以用cache了。 

 

6. 视图处理 (html.js)

  MVC构架中一个重要的功能点就是视图,简单地说就是模板处理。这里我们定义一个Html类(对应于ASP.Net MVC中的Html类)来实现这个功能。

  基本思路是:装载模板文件、调用EJS处理模板。


 1 var fs = require('fs'); 2 var path = require('path'); 3 var _ = require('underscore'); 4 var _s = require('underscore.string'); 5 var ejs = require('ejs'); 6  7 var Html = module.exports = function (controller) { 8     this.controller = controller; 9 };10 11 //处理一个模板返回结果内容12 Html.prototype.renderView = function(options) {13     //确定模板文件,若未指定,则缺省与控制器相同14     var options = options || {};15     if (!options.path || !options.name) {16         options.path = options.path || this.controller.controllerInfo.controllerName;17         options.name = options.name || this.controller.controllerInfo.methodName;18     }19     options.path = _s.trim(options.path,' \/\\');20     //模板文件必须存在于views文件夹中21     var viewPath = path.resolve(path.resolve('./views/', options.path + '/'), options.name);22     if (options.name.indexOf('.')<0) {23         options.extname = options.extname || '.htm';24         if (!_s.startsWith(options.extname,'.')) viewPath += '.';25         viewPath += options.extname;26     }27 28     try { //这里必须加try...cache,以防止文件操作及视图处理错误29         var tmpl = this.controller.cache.get(viewPath); //首先从缓存中读取模板,以提高效率30         if (!tmpl) { 31             tmpl = fs.readFileSync(viewPath);32 33             if (!_.isString(tmpl)) {34                 tmpl = tmpl.toString();35             }36 37             this.controller.cache.set(viewPath, tmpl)38         }39         40         //使用EJS引擎处理模板,并将一些可能需要的参数传入引擎41         var res = ejs.render(tmpl, {42             filename: viewPath,43             model: options.data,44             html: this.controller.html,45             session: this.controller.session46         });47 48         return res;49     }50     catch (err)    {51         console.log(_s.sprintf('Html.renderView error: %s', err));52 53         return null;54     }55 }


  Q: 为什么我要使用EJS来处理模板,而不使用更稳定更流行underscore或jade等引擎?

  A: 在选择模板处理引擎时,我重点考虑到了嵌入式开发的需求,即我应该可以将任何参数任何数据传入到模板引擎中,并参与模板的处理;而这个需求ejs做得更好。

6. 嵌入式开发 (html.js)

   嵌入式开发,即在网页文件中,嵌入并运行程序代码。例如,以下的来自于ASP.Net MVC自动生成的一份代码:


<%

    if (Request.IsAuthenticated) {%>

        欢迎使用 <b><%: Page.User.Identity.Name %></b>!

        [ <%: Html.ActionLink("注销", "LogOff", "Account") %> ]<%

    }

    else {%> 

        [ <%: Html.ActionLink("登录", "LogOn", "Account") %> ]<%

    }%>


  这其中包含三个关键点:

在页面中可以签入程序脚本,例如if...else...

页面中可以引用外部程序传入的数据对象,例如request, page, model,以及自定义对象等

支持Html.method的方式来创建页面控件

  而这些,使用ejs都可以实现。

  首先看上节代码中的第42-45行,我们在这里传入任何所需要的数据对象。同时,我们传入了一个Html对象,用于支持创建页面控件。

  Html对象除了上节代码中定义的一个基本方法renderView之外,还需要定义更多的方法(具体需要哪些方法,请参见MSDN的在线帮助System.Web.Mvc.HtmlHelper对象)。

  首先,我们需要定义一个生成控件的基本方法:  


 1 Html.prototype.element = function(tag, attributes) { 2     var buf = [], text=null; 3      4     buf.push('<'); 5     buf.push(tag); 6     if (attributes) { 7         for (var key in attributes) { 8             if (_.has(attributes, key)) { 9                 var value = attributes[key];10                 if (key.toLowerCase() === 'text')11                     text = value;12                 else if (value) 13                     buf.push(" " + key + '="' + _.escape(value) + '"');14             }15         }16     }17     if (!_.isNull(text)) {18         buf.push('>');19         buf.push(text);20         buf.push('</'+tag+'>');21     }22     else {23         buf.push('/>');24     }25 26     return buf.join('');27 }

  这个方法没什么好说的,根据数据构造一个字符串而已。

  后面我们要做的,就是一个个编写控件生成代码。(下面是一些常用控件的生成代码)

Html.elements

 

 7. 路由 (routes.js)

  路由功能是一个完整的框架必须实现的,并且框架的强壮性同此息息相关。

      但是呢,要实现一套完成的路由功能,难度确实也不低。这里,就只实现了最简单的路由功能,以解析这样的Url:

  ~/<Controller>/<Action>/<parameter1>/<parameter2>/...

 1 var url = require('url'); 2  3 module.exports._routes=[]; 4  5 //Parse the controller name and method name and additional parameters 6 module.exports.parseControllerInfo = function(req) { 7     var controllerName = null, methodName = null, parameters = null; 8      9     //this section must be optimized in future.10     var paths = url.parse(req.url).pathname.split('/');11     if (paths.length >= 1) 12         controllerName = paths[1];13     if (paths.length >= 2) 14         methodName = paths[2];15     if (paths.length >=3) {16         parameters = paths.slice(3);17     }18     19     return {20         controllerName : controllerName,21         methodName : methodName,22         parameters : parameters23     }24 }


 

五:模型(Model)的实现

  在MVC中,Model是一个不可或缺的成分。而本文所描述的框架,在Controller和View方面做了较多的努力,但是对Model则没有太多涉及。

  这意味着这个框架没能实现MVC的Model吗?当然不是。

  在我理解,MVC的Model所实现的功能是:

后台程序将数据对象传入View中,由视图使用

浏览器回传的时候,自动把传入数据(post或get)打包成对象,由后台程序使用

  针对上述需求,此框架是这样处理的:

Html.renderView方法中调用了ejs.render方法来处理模板,并传入了任何可能使用的数据

RequestHandler.handleDynamicRequest方法,把客户端传入的任何数据,打包成了一个可以直接使用的数据集合

  当然这点儿Model实现是过于简单了(不过以后可以扩展嘛)。

 

六:应用示例

  好了,以上就是本文所介绍的基于node.js的一个MVC框架的全部内容,虽然很简陋,不过确实能用。

  这里做一个简单的示例来补充一下。

  首先在controllers文件夹下新建一个test.js文件:  

 1 var _ = require('underscore'); 2 var _s = require('underscore.string'); 3 var mvc = require('mvc'); 4  5 module.exports = function(req, res) { 6     _.extend(this, mvc.controller(req, res)); 7      8     this.done = function(params, model) { 9         var data = {10             users: [11               { name: 'tj' },12               { name: 'mape' },13               { name: 'guillermo' }14             ],15             utils: {16                 caption: function (str) {17                     return _s.capitalize(str);18                 }19             },20             listOptions: function() {21                 var res=[];22                 for (var i=0;i<10;i++) {23                     res.push({value: i+'', text: (i*10)+''});24                 }25                 return res;26             }27         };28         _.extend(data, model);29         this.responseView({name: 'done.htm', data : data});30     }31 }


  注意,第3,6,28行代码是必须的,其它的根据实际需求写。

  然后在views文件夹下新建一个test文件夹,在其中新建一个done.htm文件:


 1 <html> 2 <body> 3 <form method="post"> 4 <% if (model.users && model.users.length) { %> 5   <ul> 6     <% model.users.forEach(function(item){ %> 7       <li> <%= model.utils.caption(item.name)%>: <%= item.name %></li> 8     <% }) %> 9   </ul>10 <% } %>11 <div>12 TextBox1: <%- html.textBox({name: "txtTest1", value: model.txtTest1}) %><br>13 TextBox2: <%- html.textBox({name: "txtTest2", value: model.txtTest2, multi: true}) %><br>14 CheckBox1: <%- html.checkBox({name: "chkTest1", value: "dd", checked: model.chkTest1}) %><br>15 CheckBox2: <%- html.checkBox({id: "chkTest2", name: "chkTest2", value: "dd", checked: model.chkTest2}) %><%- html.label({for: "chkTest2", text: "Text for Check box"}) %><br>16 DropDownList: <%- html.listBox({name: "lstTest1", value: model.lstTest1, options: [{value:1, text:"aa"},{value:2, text:"dd"}]}) %><br>17 List Box: <%- html.listBox({name: "lstTest2", value: model.lstTest2, size: 4, options: model.listOptions}) %><br>18 <%- html.button({type: "submit"}) %>19 </div>20 </form>21 </body>22 </html>

 

  启动node.js:node index.js,在浏览器中输入地址://localhost:88/test/done

  然后就可以看到如下结果:  

Node.js教程 模仿ASP.Net MVC的node.js MVC功能实现

   希望这篇文章可以帮助到你。总之,同学们,你想要的职坐标IT频道都能找到!


本文由 @小作 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程