如何一体化一个NodeJs的MVC开发框架

如何一体化一个NodeJs的MVC开发框架

本文章在本人个人NodeJs论坛已经发布:www.tnodejs.com<br/> 希望得到NAE的邀请码!<br/> 本小型框架,可提供给各个开发者进行参考,里面涉及到一些NodeJs如何去实现继承<br/> 同时如何处理路由请求的问题,还有Mysql数据库链接,可能对于PHP开发者会非常的得心应手

<div style=‘text-align:center’><h1>NodeJs之MyWeb框架开发介绍</h1></div>

<br/> 本框架适合使用NodeJs进行web开发的MVC框架模式,本框架使用了express框架作为nodejs的web开发支撑,使用mysql作为数据库开发源,下面我们就简单的介绍如何利用本框架进行一个简单的web应用开发。当然本框架并非官方,也并非专业设计,希望开发者共同来把本框架设计好,以便我们可以在国内实现一个NodeJs的Web开发框架。<br/> 一、项目文件夹介绍<br/> 项目文件夹主要是根据传统的MVC设计模式,设计出来的框架。<br/> enter image description here <br/>二、 入口文件介绍 <br/>本框架的入口文件为index.js,该入口你可以添加多种全局静态变量,例如你所需要的各个文件夹路径,以及一些模块。<br/> 举例如下:<br/> <pre>

 //========================全局变量定义===============================

global.BASE_DIR = __dirname; global.APP = global.BASE_DIR + “/application/”; global.CON = global.APP + “/controller/”; global.CORE = global.APP + “/core/”; global.MODEL = global.APP + “/model/”; global.CONF = global.BASE_DIR + “/conf/”; global.log = global.BASE_DIR + “/log/”; global.PUBLIC = global.BASE_DIR + “/public/”; global.VIEW = global.BASE_DIR + “/view/”; /**

  • modules引入 */ global.express = require(‘express’); global.sio = require(‘socket.io’); global.fs=require(‘fs’); global.path = require(‘path’); global.url = require(‘url’); global.parseCookie = require(‘connect’).utils.parseCookie; global.MemoryStore = require(’./node_modules/connect/lib/middleware/session/memory’); global.Session = require(’./node_modules/connect/lib/middleware/session/session’); global.sys = require(‘util’); </pre>

代码2-1:index.js


<br/> 在index.js中你需要将你所有的文件夹路径、模块使用全局变量进行替换,该方法的优势在于,避免用户在编码中引入过长的文件路径,只需要使用简单的变量进行替换。<br/> urlResolve = require(CORE + “url_resolve”); urlResolve.getActionInfo(); 代码:2-2:路由处理逻辑<br/> 本代码包含进逻辑处理类,同时应用逻辑处理类中的getActionInfo方法,创建服务器,并且处理url请求逻辑。<br/> 三、 路由处理逻辑<br/> 主要有六个方法,其中的getActionInfo是exports,其他方法均为私有方法。<br/> <pre> exports.getActionInfo = function(){ systemConfig(); app.get(’/:key’, function(req, res){ callUrlRequest(req, res); }); app.post(’/:key’, function(req, res){ callUrlRequest(req, res); }); listenPort(); }; function callUrlRequest(req, res){ var routerMsg = getUrlConf(); var key = req.params.key; var session = checkSession(req, key); if(key == “favicon.ico”){return;}; if(session == 0){ res.redirect(’/index’); return; } console.log("[key:"+ key +"] " + “[class:” + routerMsg[key].cla + “] " + “[controller:” + routerMsg[key].fun +”]"); require(CON + routerMsg[key].con); var controllerObj = eval("new " + routerMsg[key].cla); controllerObj.init(req, res); controllerObj[routerMsg[key].fun].call(); } </pre> <span style=‘text-align:center’>代码2-3:路由处理getActionInfo</span><br/> SystemConfig是配置express框架的相应数据,配置静态文件夹以及express框架的相应配置数据。之后添加两种url请求方式,分别是get和post方法,由于两种方法请求资源的路由处理都是一样的,因此使用callUrlRequest来处理。<br/> callUrlRequest 获取路由配置文件信息getUrlConf;<br/> 2、获取当前访问的key值,根据key值得到相应的配置信息,配置文件可以展示如下:<br/> <pre> “test”: { “con” : “test”, “cla” : “test”, “fun” :“test” }, “favicon.ico” : { “con” : “”, “cla” : “”, “fun” : “” }, “login” : { “con” : “index_controller”, “cla” : “IndexController”, “fun” : “loginAct” }, “index” : { “con” : “index_controller”, “cla” : “IndexController”, “fun” : “loginPageAct” }, “loginS” : { “con” : “index_controller”, “cla” : “IndexController”, “fun” : “toMainPageAct” } } </pre> <span style=‘center’>代码2-4:配置文件信息</span><br/> 如果当前key为test那么我们就可以得到相应的controller、class和function。同时因为nodejs服务器每次请求数据的时候都会加入favicon.ico,因此在代码中我们需要将其剔除。对于checkSession就是验证登录信息。<br/>

  1. 得到controller、class和function,首先require相应的controller,然后使用eval来new相应对象,使用controllerObj[routerMsg[key].fun].call();该方法进行调用。(本部分处理,涉及到一个JavaScript的小技巧,如何对一个字符串进行new,同时调用一个对象的方法,该方法名为字符串变量)<br/>
  2. 最后就是listenPort();进行监听事件,也是服务器开始启动。这样一个基本的路由处理就完成实现了。<br/> 四、 数据层实现<br/> 本系统数据层基类是在core文件夹下的base_model.js,该类主要包含数据库的一般方法,主要含有数据库链接、数据库操作基本方法add、update、deleteItem、query、select等,具体实现方式,就不细讲。<br/> BaseModel为基类,其他对应于相应的表的类都继承来自BaseModel基类<br/> 继承方法使用JavaScript的原型继承:<br/> <pre> IndexController.prototype = new BaseController(); global.IndexController = IndexController; </pre> 五、 逻辑层实现<br/> 类同于数据层的实现方法,其继承都是来自于基类BaseController,BaseController现只包含三个方法:init、displayHtml、displayJade。<br/> 六、 代码规范<br/> 本框架不要求开发者是如何去定义代码规范,但本框架实现的代码规范是如下:<br/> 变量命名:私有变量统一使用”_name”,全局变量使用大写”VIEW”,简单变量请使用骆驼峰”myName”<br/> 方法命名:所有方法请使用骆驼峰”getUrlRequest”<br/> 类命名:统一使用首字母大写骆驼峰”BaseController”<br/> 文件命名:统一使用下划线分割,类使用下划线分割base_controller.js<br/> 总结:整体上就可以实现一个MVC开发的MyWeb框架,其中的方法以及实现都还是处于稚嫩期,希望有开发者愿意加入,并且能够团队合作开发出我们国内优秀的NodeJs的MVC框架。 <br/>

框架代码下载地址github:https://github.com/tnodejs/myweb-nodejs 希望大家是一起交流学习,这里还有很多不足,希望大家一同改进~


19 回复

如何一体化一个NodeJs的MVC开发框架

本文章在本人个人NodeJs论坛已经发布:www.tnodejs.com

希望得到NAE的邀请码!

本小型框架,可提供给各个开发者进行参考,里面涉及到一些NodeJs如何去实现继承,同时如何处理路由请求的问题,还有Mysql数据库链接,可能对于PHP开发者会非常的得心应手。


NodeJs之MyWeb框架开发介绍

本框架适合使用NodeJs进行web开发的MVC框架模式,本框架使用了express框架作为nodejs的web开发支撑,使用mysql作为数据库开发源。下面我们就简单的介绍如何利用本框架进行一个简单的web应用开发。当然本框架并非官方,也并非专业设计,希望开发者共同来把本框架设计好,以便我们可以在国内实现一个NodeJs的Web开发框架。


一、项目文件夹介绍

项目文件夹主要是根据传统的MVC设计模式,设计出来的框架:

- application/
  - controller/
  - model/
  - view/
- core/
- conf/
- log/
- public/
- routes/
- views/
- index.js

二、 入口文件介绍

本框架的入口文件为index.js,该入口你可以添加多种全局静态变量,例如你所需要的各个文件夹路径,以及一些模块。

//========================全局变量定义===============================
global.BASE_DIR = __dirname;
global.APP      = global.BASE_DIR + "/application/";
global.CON      = global.APP + "/controller/";
global.CORE     = global.APP + "/core/";
global.MODEL    = global.APP + "/model/";
global.CONF     = global.BASE_DIR + "/conf/";
global.LOG      = global.BASE_DIR + "/log/";
global.PUBLIC   = global.BASE_DIR + "/public/";
global.VIEWS    = global.BASE_DIR + "/views/";

/** 
 * modules引入
 */
global.express = require('express');
global.mysql = require('mysql');
global.fs = require('fs');
global.path = require('path');
global.url = require('url');
global.parseCookie = require('connect').utils.parseCookie;
global.MemoryStore = require('./node_modules/connect/lib/middleware/session/memory');
global.Session = require('./node_modules/connect/lib/middleware/session/session');
global.sys = require('util');

// 初始化Express应用
const app = express();

// 配置Express
app.use(express.static(global.PUBLIC));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// 路由解析
require(global.ROUTES + 'routes.js')(app);

// 启动服务器
const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

三、 路由处理逻辑

主要包含六个方法,其中的getActionInfo是导出的,其他方法均为私有方法。

// routes.js
exports.getActionInfo = function(app) {
  const urlResolve = require(global.CORE + 'url_resolve');
  urlResolve.getActionInfo(app);
};

function systemConfig() {
  // Express配置
}

function listenPort() {
  // 监听端口
}

function callUrlRequest(req, res) {
  var routerMsg = require(global.CONF + 'router_conf.json');
  var key = req.params.key;
  if (key === "favicon.ico") { return; }
  
  console.log(`[key:${key}] [class:${routerMsg[key].cla}] [controller:${routerMsg[key].fun}]`);
  
  const Controller = require(`${global.CON}${routerMsg[key].con}`);
  const controllerObj = new Controller();
  controllerObj.init(req, res);
  controllerObj[routerMsg[key].fun]();
}

exports.getActionInfo = function(app) {
  systemConfig();
  app.get('/:key', (req, res) => {
    callUrlRequest(req, res);
  });
  app.post('/:key', (req, res) => {
    callUrlRequest(req, res);
  });
  listenPort();
};

四、 数据层实现

本系统数据层基类是在core文件夹下的base_model.js,该类主要包含数据库的一般方法,主要含有数据库链接、数据库操作基本方法addupdatedeleteItemqueryselect等。

// base_model.js
const mysql = require('mysql');

class BaseModel {
  constructor() {
    this.connection = mysql.createConnection({
      host: 'localhost',
      user: 'root',
      password: 'password',
      database: 'mydb'
    });
    this.connection.connect();
  }

  add(data) {
    // 添加数据
  }

  update(id, data) {
    // 更新数据
  }

  deleteItem(id) {
    // 删除数据
  }

  query(sql) {
    // 查询数据
  }

  select(tableName, columns) {
    // 选择数据
  }
}

module.exports = BaseModel;

五、 逻辑层实现

类同于数据层的实现方法,其继承都是来自于基类BaseControllerBaseController现只包含三个方法:initdisplayHtmldisplayJade

// base_controller.js
const BaseModel = require('../core/base_model');

class BaseController extends BaseModel {
  init(req, res) {
    // 初始化
  }

  displayHtml(viewPath, data) {
    // 渲染HTML视图
  }

  displayJade(viewPath, data) {
    // 渲染Jade视图
  }
}

module.exports = BaseController;

六、 代码规范

本框架不要求开发者是如何去定义代码规范,但本框架实现的代码规范是如下:

  • 变量命名:私有变量统一使用 _name,全局变量使用大写 VIEW,简单变量请使用骆驼峰 myName
  • 方法命名:所有方法请使用骆驼峰 getUrlRequest
  • 类命名:统一使用首字母大写骆驼峰 BaseController
  • 文件命名:统一使用下划线分割,类使用下划线分割 base_controller.js

总结:整体上就可以实现一个MVC开发的MyWeb框架,其中的方法以及实现都还是处于稚嫩期,希望有开发者愿意加入,并且能够团队合作开发出我们国内优秀的NodeJs的MVC框架。

框架代码下载地址GitHub:https://github.com/tnodejs/myweb-nodejs

希望大家是一起交流学习,这里还有很多不足,希望大家一同改进~


这结构不错,routing是在controller里面配置的?

有点 magento 的味道,很好很强大

routing的话是直接写在配置文件的!通过读取配置文件信息,来routing相应controller里面的function

每次请求时都要对router.json进行readFileSync?这个没必要吧?

那可以入去做实现呢~我觉得这样也不怎么好!的确是每次请求都需要读取配置信息!是不是可以应该直接存储为global作为静态变量!

谢谢提醒!哈哈~

挺不错的规范

在global下注册那么多名字合适么… 还是注册个命名空间吧… global.xxxxApp = {}

嗯~!很有道理!谢谢提醒~

目前我就使用吴哥写的rrestjs框架,再融合我做的aop和orm,感觉够用了~

强啊,都能抽象框架了!

现在进一步更新~互相学习!

嗯~我觉的框架都可以去搭建!我也只是学习学习~呵呵!也希望大家互相交流!

感觉这样路由表会很大

嗯!说滴很对!呵呵~这种路由设计方法是不合理的!后面我还做了改动!不过没有去维护了!

数据库没有用连接池?

简单接触了下nodejs,发现除了“异步”这个不习惯外,还有“配路由”这个也不习惯,PHP的开发者来说,WEB开发基本都是用“路由规则”来路由,而不用去配的…

我前些天也试图去实现一个基于nodejs的web开发“框架”,虽然它极不完善,甚至是dao都没实现,但却代表了一个多年WEB开发经历的人对WEB开发框架的一个期望

由于我对nodejs的理解很皮毛,所以仅仅是我学习nodejs(或者说仅仅是想了解)过程的各种简单的拼凑

有兴趣的可以看看 https://github.com/tim1020/ec

  1. 区别于大多数框架的人工配置路由表,ec使用规则路由(三种规则可配 /c/m ,/c.m, /?c=c&m=m),即 访问/c/m时,自动路由到controler下的c.js并执行m方法
  2. 请求处理req对象,框架已初始化req对象,在controller中可直接使用该对象获取各种请求内容,包括 post,get,cookie,同时支持mulitpart的解释(即支持post上传文件的解释,直接用req.files获取上传文件内容)
  3. 响应处理res对象,包括setCookie,showErr以及模板输出render,在controller中当完成业务逻辑后,调用该对象来设置响应内容,可以自定义模板引擎,前置条件是使用该引擎使用renderFile(tplfile,data)来渲染转出(没有该方法可自行二次封装)
  4. 会话管理session,实现了set,get,getAll,unset方法,支持自定义session handler,只需要实现约定的方法即可
  5. 日志处理log,支持分级,支持设置控制台输出或文件输出,可单独关闭框架debuglog,内置accesslog输出
  6. 静态文件服务(支持304)

要一体化一个Node.js的MVC开发框架,我们可以按照以下步骤来构建:

一、项目结构

项目应该遵循MVC(模型-视图-控制器)模式,标准的项目结构如下:

my-web-app/
├── application/
│   ├── controller/
│   │   └── index_controller.js
│   ├── model/
│   │   └── base_model.js
│   └── view/
│       └── index.jade
├── core/
│   └── base_controller.js
├── conf/
│   └── routes.json
├── public/
│   └── static_files/
├── log/
└── index.js

二、入口文件 index.js

// 全局变量定义
global.BASE_DIR = __dirname;
global.APP = global.BASE_DIR + "/application/";
global.CON = global.APP + "/controller/";
global.CORE = global.APP + "/core/";
global.MODEL = global.APP + "/model/";
global.CONF = global.BASE_DIR + "/conf/";
global.log = global.BASE_DIR + "/log/";
global.PUBLIC = global.BASE_DIR + "/public/";
global.VIEW = global.BASE_DIR + "/view/";

// 引入模块
global.express = require('express');
global.mysql = require('mysql');
global.path = require('path');
global.url = require('url');

// Express app 初始化
const app = express();

// 加载路由配置
const routeConfig = require(`${CONF}routes.json`);

// 路由处理函数
app.use((req, res) => {
    const key = req.params.key;
    if (routeConfig[key]) {
        const { con, cla, fun } = routeConfig[key];
        require(`${CON}${con}`);
        const controller = new global[cla]();
        controller.init(req, res);
        controller[fun]();
    } else {
        res.status(404).send('Not Found');
    }
});

// 监听端口
app.listen(3000, () => console.log('Server started on port 3000'));

三、路由配置文件 routes.json

{
    "index": {
        "con": "index_controller",
        "cla": "IndexController",
        "fun": "indexAction"
    },
    "login": {
        "con": "login_controller",
        "cla": "LoginController",
        "fun": "loginAction"
    }
}

四、数据层实现 base_model.js

const mysql = require('mysql');

class BaseModel {
    constructor() {
        this.connection = mysql.createConnection({
            host: 'localhost',
            user: 'root',
            password: '',
            database: 'mydb'
        });
        this.connection.connect();
    }

    query(sql) {
        return new Promise((resolve, reject) => {
            this.connection.query(sql, (err, results) => {
                if (err) return reject(err);
                resolve(results);
            });
        });
    }
}

module.exports = BaseModel;

五、逻辑层实现 base_controller.js

class BaseController {
    constructor() {}

    init(req, res) {
        this.req = req;
        this.res = res;
    }

    displayHtml(html, data = {}) {
        this.res.render(html, data);
    }

    displayJson(data) {
        this.res.json(data);
    }
}

module.exports = BaseController;

六、控制器 index_controller.js

const BaseController = require(`${CORE}base_controller.js`);

class IndexController extends BaseController {
    constructor() {
        super();
    }

    indexAction() {
        this.displayHtml('index', { message: 'Hello World' });
    }
}

module.exports = IndexController;

以上代码展示了如何创建一个简单的Node.js MVC框架。通过这种方式,我们可以更清晰地组织代码,提高代码的可维护性和复用性。

回到顶部