koa-grace:一個基于koa的node多應用MVC框架
來自: https://segmentfault.com/a/1190000004465603
春節期間沒回家留在北京寫了一個基于koa的node MVC框架: koa-grace ,大家有興趣可以star & fork下,謝謝支持啦!!
項目地址:
https://github.com/xiongwilee/koa-grace
詳細文檔:
1. 簡介
koa-grace是基于 koa 1.x 的Nodejs多站點MVC框架。
1.1 特征
為什么koa-grace是新一代Nodejs MVC框架:
-
一個node服務,多個站點應用;
-
yield 異步語法支持,忘掉回調噩夢;
-
繼承koa中間件,擴展性更強;
-
支持 路徑即路由 ,更優雅的路由方式;
-
RESTful數據代理支持,前后端完全解耦;……
1.2 目錄結構
├── app // 站點總目錄 │ ├── blog // 站點:blog目錄 │ │ ├── controller // 站點:blog的路由(控制器)目錄 │ │ ├── model // 站點:blog的模型目錄,包括公共控制器、mongo等 │ │ ├── static // 站點:blog的靜態文件目錄 │ │ └── views // 站點:blog的html模板目錄 │ ├── reactjs-boilerplate // 站點:reactjs-boilerplate目錄 │ └── shop // 站點:shop目錄 ├── bin // server啟動器目錄 │ ├── koa-grace // TODO:命令行工具 │ └── server.js // server啟動器 ├── config // 配置文件目錄 │ └── main.js // 主配置文件 ├── package.json └── src // 核心文件 └── app.js // 主文件
2. 快速開始
在開始使用koa-grace之前請確保您已經安裝并運行了下面的工具:
-
Nodejs (v4+)
-
MongoDB (DEMO 演示使用,正式環境可以配置不用)
2.1 安裝
$ git clone https://github.com/xiongwilee/koa-grace.git $ cd koa-grace && npm install
2.2 配置
在koa-grace目錄下,打開配置文件:
$ vi config/main.js
修改配置項: config.mongo.blog 為您的本地mongoDB的路徑:
// mongo configuration mongo: { 'blog': 'mongodb://localhost:27017/blog' }
**更多關于配置項的文檔可以看下面的使用文檔。
2.3 運行
在koa-grace目錄下執行,可能需要root權限:
$ npm run dev
然后訪問 http://127.0.0.1 :3000 ,就可以看到koa-grace其中的一個案例站點了!
您也可以訪問koa-grace的一個線上應用: http://mlsfe.biz 。
3. 詳細使用文檔
雖說koa-grace是一個完整的MVC框架 , 但其本質是基于一種多站點解決方案的koa中間件的集合。其核心中間件包括但不僅限于: koa-router , koa-views , koa-mount , koa-static , koa-grace-vhost , koa-grace-router , koa-grace-proxy , koa-grace-model , koa-grace-mongo , ...
3.1 多站點配置 - Vhost
koa-grace是基于 koa-grace-vhost 進行vhost管理,基本原理是:
一個域名對應一個應用,一個應用對應一個目錄
如此一來,配置多站點就很簡單了,在配置文件 config/main.js 中:
// vhost配置 vhost: { 'test.mlsfe.biz':'blog', '127.0.0.1':'blog', 'localhost':'shop', '0.0.0.0':'reactjs-boilerplate' }
其中,vhost配置中 127.0.0.1 是URI的 hostname , blog 是站點總目錄app下的一個目錄 app/blog 。如果當前請求URI為: http://127.0.0.1/home 則koa-grace會自動訪問 app/blog 目錄里的路由、模型、靜態文件。
需要說明的是, 多站點配置僅以URI的hostname為主鍵 ;也就是說,訪問帶端口號的 http://127.0.0.1 :3000/home 也會定位到app/blog目錄。
3.2 路由及控制器 - Router&Controller
很好,如果你配置好了一個vhost為 '127.0.0.1':'blog' , koa-grace就會自動生成一個vhost到 app/blog 目錄了!接下來,進入 app/blog/controller 目錄進行路由配置。
koa-grace基于 koa-grace-router 進行路由管理的,koa-grace-router又是依賴于: koa-router 。
3.2.1 文件路徑即路由
以 blog 站點為例, koa-grace-router 會找到 app/blog/* 目錄下的所有 .js 后綴的文件,并以文件路徑生成路由。我們再看一下案例中 blog 的路由文件:
├── api │ └── post.js ├── dashboard │ ├── post.js │ ├── site.js │ ├── user.js │ └── userAuthor.js ├── error.js ├── home.js ├── post.js └── user.js
如果當前請求URI為: http://127.0.0.1/dashboard/post/ * 則路由將自動落在 dashboard/post.js 文件中。
那么,如果請求路徑如果是 http://127.0.0.1/dashboard/post/list ,這個 dashboard/post.js 文件是如何控制的呢?
3.2.2 路由文件詳細說明
打開 app/blog/controller/dashboard/post.js 文件:
/.../ exports.list = function* () { // 綁定默認控制器 yield this.bindDefault(); // 獨立權限控制 if (!userAuthor.checkAuth(this, this.userInfo)) {return};// 獲取請求query參數 let pageNum = this.query.page; // 獲取數據 let PostModel = this.mongo('Post'); let posts = yield PostModel.page(pageNum,20); let page = yield PostModel.count(pageNum,20);
// 渲染模板 yield this.render('dashboard/post_list',{ breads : ['文章管理','文章列表'], posts:posts, page:page, userInfo: this.userInfo, siteInfo: this.siteInfo }) } exports.list.method = 'get'; exports.list.regular = null; /.../</pre>
對,就是你猜的那樣:koa-grace-router是通過post.js的module.exports進行下一步的路由控制。
另外,需要說明以下幾點:
-
如果需要配置dashboard/post/list請求為 POST 方法,則post.js中聲明 exports.list.__method__ = 'post' 即可(不聲明默認為get請求),更多方法類型請參看: koa-router#routergetputpostpatchdelete--router ;
-
如果要進一步配置dashboard/post/list/id路由,則在post.js中聲明 exports.list.__regular__ = '/:id'; 即可,更多相關配置請參看: koa-router#named-routes
-
如果當前文件路由就是一個獨立的控制器,則 module.exports 返回一個yield方法即可,可以參考案例 blog 中的 controll/home.js
-
如果當前文件僅僅是一個依賴,僅僅被其他文件引用;則在文件中配置 exports.__controller__ = false ,該文件就不會生成路由了
當然,如果一個路由文件中的控制器方法都是post方法,您可以在控制器文件最底部加入: module.exports.__method__ = 'post' 即可。 __regular__ 的配置同理。
3.2.3 控制器
剛剛我們看到了post.js中的exports.list方法,事實上它就是一個控制器(controller)了。
您可以新建一個 app/blog/controller/hello.js 文件
exports.koagrace = funtion* (){ this.body = 'hello koa-grace!'; }
訪問 http://127.0.0.1/hello/koagrace ,就可以看到“hello koa-grace!”輸出。它是典型的一個基于上下文(context)的yield方法。幾個關鍵方法/參數使用如下:
context屬性 | 類型 | 說明 |
---|---|---|
this.query | object | get參數 |
this.request.body | object | post參數,由于koa-grace默認引入了 koa-bodypaser ,您可以直接在this.request.body中獲取到post參數 |
this.bindDefault | function | 公共控制器,相當于 require('app/blog/model/defaultCtrl.js') |
this.render | function | 模板引擎渲染方法,請參看:3.5 模板引擎- Template engine |
this.mongo | function | 數據庫操作方法,請參看:3.3 數據庫 - Database |
this.mongoMap | function | 并行數據庫多操作方法,請參看:3.3 數據庫 - Database |
this.proxy | function | RESTful數據請求方法,請參看:3.4.1 數據代理 |
this.download | function | 文件請求代理方法,請參看:3.4.2 請求代理 |
this.upload | function | 文件上傳方法,請參看: 3.4.3 文件上傳 |
更多context文檔請參看 koa官網 ,或 http://koajs.in/doc/
3.3 數據庫 - Database
koa-grace引入基于 mongoose 的 koa-grace-mongo ,可以非常便捷地使用mongoDB。
3.3.1 連接數據庫
在配置文件 config/main.js 中進行配置:
// mongo配置 mongo: { options:{ // mongoose 配置 }, api:{ 'blog': 'mongodb://localhost:27017/blog' } },
其中, mongo.options 配置mongo連接池等信息, mongo.api 配置站點對應的數據庫連接路徑。
值得注意的是, 配置好數據庫之后,一旦koa-grace server啟動mongoose就啟動連接,直到koa-grace server關閉
3.3.2 mongoose的schema配置
依舊以案例 blog 為例,參看 app/blog/model/mongo 目錄:
└── mongo ├── Category.js ├── Link.js ├── Post.js └── User.js
一個js文件即一個數據庫表即相關配置,以 app/blog/model/mongo/Category.js :
'use strict';// model名稱,即表名 let model = 'Category';
// 表結構 let schema = [{ id: {type: String,unique: true,required: true}, name: {type: String,required: true}, numb: {type: Number,'default':0} }, { autoIndex: true, versionKey: false }];
// 靜態方法:http://mongoosejs.com/docs/guide.html#statics let statics = {}
// 方法擴展 http://mongoosejs.com/docs/guide.html#methods let methods = { /**
- 獲取博客分類列表 / list: function () { return this.model('Category').find(); } }
module.exports.model = model; module.exports.schema = schema; module.exports.statics = statics; module.exports.methods = methods;</pre>
主要有四個參數:
-
model , 即表名,最好與當前文件同名
-
schema , 即mongoose schema
-
methods , 即schema擴展方法, 推薦把數據庫元操作都定義在這個對象中
-
statics , 即靜態操作方法
3.3.3 在控制器中調用數據庫
在控制器中使用非常簡單,主要通過 this.mongo , this.mongoMap 兩個方法。
1) this.mongo(name)
調用mongoose Entity對象進行數據庫CURD操作
參數說明:
@param [string] name : 在 app/blog/model/mongo 中配置Schema名,
返回:
@return [object] 一個實例化Schema之后的Mongoose Entity對象,可以通過調用該對象的methods進行數據庫操作
案例
參考上文中的Category.js的配置,以 app/blog/controller/dashboard/post.js 為例,如果要在博客列表頁中獲取博客分類數據:
// http://127.0.0.1/dashboard/post/list exports.list = function* (){ let cates = yield this.mongo('Category').list(); this.body = cates; }
2) this.mongoMap(option)
并行多個數據庫操作
參數說明
@param [Object] option[].model mongoose Entity對象,通過this.mongo(model)獲取
@param [function] option[].fun mongoose Entity對象方法
@param [array] option[].arg mongoose Entity對象方法參數
返回
@return [array] 數據庫操作結果,以對應數組的形式返回
案例
let PostModel = this.mongo('Post'); let mongoResult = yield this.mongoMap([{ model: PostModel, fun: PostModel.page, arg: [pageNum] },{ model: PostModel, fun:PostModel.count, arg: [pageNum] }]);let posts = mongoResult[0];// 獲取第一個查詢PostModel.page的結果 let page = mongoResult[1]; // 獲取第二個查詢PostModel.count的結果,兩者并發執行</pre>
3.4 代理 - Proxy
除了在控制器中直接進行數據庫操作,Web應用還有可能由其他服務進行后端部署。針對這種場景,koa-grace引入了基于 Request 的 koa-grace-proxy 。
3.4.1 數據代理
在koa-grace的控制器中使用 this.proxy 方法進行數據代理非常簡單:
exports.list = function* (){ yield this.proxy({ userInfo:'github:post:user/login/oauth/access_token?client_id=**', otherInfo:'github:other/info?test=test', });console.log(this.backData); /** * { * userInfo : {...}, * otherInfo : {...} * } */
}</pre>
你也可以不傳 this.backData 參數,默認注入到上下文的 this.backData 對象中:
exports.list = function* (){ yield this.proxy({ userInfo:'github:post:user/login/oauth/access_token?client_id=**', otherInfo:'github:other/info?test=test', });console.log(this.backData); /** * { * userInfo : {...}, * otherInfo : {...} * } */
}</pre>
另外, github:post:user/login/oauth/access_token?client_id=**** 說明如下:
-
github : 為在 config.main.js 的 api 對象中進行配置;
-
post : 為數據代理請求的請求方法,該參數可以不傳,默認為 get
-
path : 后面請求路徑中的query參數會覆蓋當前頁面的請求參數(this.query),將query一同傳到請求的接口
-
你也可以寫完整的路徑: {userInfo:'https://api.github.com/user/login?test=test'}
3.4.2 文件代理
文件請求代理也很簡單,比如如果需要從github代理一個圖片請求返回到瀏覽器中,參考: http://mlsfe.biz/user/avatar?img=https ://avatars.githubusercontent.com/u/1962352?v=3
exports.avatar = function* (){ let imgUrl = query.img; yield this.download(imgUrl); }
3.4.3 文件上傳
TODO: 文件上傳并代理到其他服務或者存儲到本地
3.5 模板引擎- Template engine
koa-grace引入 koa-views , 進行模板引擎管理。默認的模板引擎為 swig , 您可以在 config/main.js 中配置 template 屬性您想要模板引擎:
// 模板引擎配置 template: 'swig'
目前支持的模板引擎列表在這里: consolidate.js#supported-template-engines
在控制器中調用 this.render 方法渲染模板引擎:
exports.home = function* () { yield this.bindDefault();yield this.render('dashboard/site_home',{ breads : ['站點管理','通用'], userInfo: this.userInfo, siteInfo: this.siteInfo }) }</pre>
模板文件在 app/blog/views 目錄中。
3.6 靜態文件服務 - Static server
koa-grace引入 koa-mount 及 koa-static ,將靜態文件代理到 /static :
// 配置靜態文件路由 vapp.use(mount('/static', koastatic(appPath + '/static') ));以案例中 blog 的靜態文件為例,靜態文件在blog項目下的路徑為: app/blog/static/image/bg.jpg ,則訪問路徑為 http://127.0.0.1/static/image/bg.jpg 。
3.7 啟動服務及調試 - Process & DEBUG
3.7.1 開發環境
在開發環境可以使用npm命令完成。
1) 普通啟動:
$ npm run start2) watch啟動:
$ npm run dev在80端口啟動
$ npm PORT=80 run dev
DEBUG模式啟動,默認為DEBUG=koa-grace*
$ npm DEBUG=* run dev </pre>
3.7.2 生產環境
在生產環境推薦使用 pm2 進行進程管理:
$ npm install pm2 -g $ pm2 start node ./bin/server.js更多使用方法,請參看 pm2 。
5. 貢獻
-
歡迎提PR
-
給作者提問&提建議: xiongwilee@foxmail.com
</ul> </div>