1. 程式人生 > >淺談KOA2 Restful方式路由初探

淺談KOA2 Restful方式路由初探

outer 根據 集合 pes str readdir 選擇 淺談 types

前言

最近考慮將服務器資源整合一下,作為多端調用的API

看到Restful標準和ORM眼前一亮,但是找了不少版本路由寫的都比較麻煩,於是自己折騰了半天

API庫結構

考慮到全部對象置於頂層將會造成對象名越來長,同時不便於維護,故采取部分的分層結構

如workflow模塊內的prototypes,instances等等,分層的深度定義為層級

可訪問的對象集合(collection)的屬性滿足Restful設計

?
1 2 3 4 5 6 7 8 9 10 -- workflow(category) -- prototypes(collection)
-- [method] ... -- [method] ... -- instances(collection) -- users(collection) --[method] List #get :object/ --[method] Instance #get :object/:id -- ... -- ...

RESTFUL API 接口

將Restful API接口進行標準化命名

?
1 2 3 4 5 6 7 8 9 10 .get(‘/‘
, ctx=>{ctx.error(‘路徑匹配失敗‘)}) .get(‘/:object‘, RestfulAPIMethods.List) .get(‘/:object/:id‘, RestfulAPIMethods.Get) .post(‘/:object‘, RestfulAPIMethods.Post) .put(‘/:object/:id‘, RestfulAPIMethods.Replace) .patch(‘/:object/:id‘, RestfulAPIMethods.Patch) .delete(‘/:object/:id‘, RestfulAPIMethods.Delete)
.get(‘/:object/:id/:related‘, RestfulAPIMethods.Related) .post(‘/:object/:id/:related‘, RestfulAPIMethods.AddRelated) .delete(‘/:object/:id/:related/:relatedId‘, RestfulAPIMethods.DelRelated)

API對象

這個文件是來自微信小程序demo,覺得很方便就拿來用了,放於需要引用的根目錄,引用後直接獲得文件目錄結構API對象

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 const _ = require(‘lodash‘) const fs = require(‘fs‘) const path = require(‘path‘) /** * 映射 d 文件夾下的文件為模塊 */ const mapDir = d => { const tree = {} // 獲得當前文件夾下的所有的文件夾和文件 const [dirs, files] = _(fs.readdirSync(d)).partition(p => fs.statSync(path.join(d, p)).isDirectory()) // 映射文件夾 dirs.forEach(dir => { tree[dir] = mapDir(path.join(d, dir)) }) // 映射文件 files.forEach(file => { if (path.extname(file) === ‘.js‘) { tree[path.basename(file, ‘.js‘)] = require(path.join(d, file)) tree[path.basename(file, ‘.js‘)].isCollection = true } }) return tree } // 默認導出當前文件夾下的映射 module.exports = mapDir(path.join(__dirname))

koa-router分層路由的實現

創建多層路由及其傳遞關系

執行順序為

1 -- 路徑匹配
-- 匹配到‘/‘結束
-- 匹配到對應的RestfulAPI執行並結束
-- 繼續
2 -- 傳遞中間件 Nest
3 -- 下一級路由
4 -- 循環 to 1

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 const DefinedRouterDepth = 2 let routers = [] for (let i = 0; i < DefinedRouterDepth; i++) { let route = require(‘koa-router‘)() if (i == DefinedRouterDepth - 1) { // 嵌套路由中間件 route.use(async (ctx, next) => { // 根據版本號選擇庫 let apiVersion = ctx.headers[‘api-version‘] ctx.debug(`------- (API版本 [${apiVersion}]) --=-------`) if (!apiVersion) { ctx.error(‘版本號未標記‘) return } let APIRoot = null try { APIRoot = require(`../restful/${apiVersion}`) } catch (e) { ctx.error (‘API不存在,請檢查Header中的版本號‘) return } ctx.debug(APIRoot) ctx.apiRoot = APIRoot ctx.debug(‘---------------------------------------------‘) // for(let i=0;i<) await next() }) } route .get(‘/‘, ctx=>{ctx.error(‘路徑匹配失敗‘)}) .get(‘/:object‘, RestfulAPIMethods.List) .get(‘/:object/:id‘, RestfulAPIMethods.Get) .post(‘/:object‘, RestfulAPIMethods.Post) .put(‘/:object/:id‘, RestfulAPIMethods.Replace) .patch(‘/:object/:id‘, RestfulAPIMethods.Patch) .delete(‘/:object/:id‘, RestfulAPIMethods.Delete) .get(‘/:object/:id/:related‘, RestfulAPIMethods.Related) .post(‘/:object/:id/:related‘, RestfulAPIMethods.AddRelated) .delete(‘/:object/:id/:related/:relatedId‘, RestfulAPIMethods.DelRelated) if (i != 0) { route.use(‘/:object‘, Nest, routers[i - 1].routes()) } routers.push(route) } let = router = routers[routers.length - 1]

Nest中間件

將ctx.apiObject設置為當前層的API對象

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const Nest= async (ctx, next) => { let object = ctx.params.object let apiObject = ctx.apiObject || ctx.apiRoot if(!apiObject){ ctx.error(‘API裝載異常‘) return } if (apiObject[object]) { ctx.debug(`ctx.apiObject=>ctx.apiObject[object]`) ctx.debug(apiObject[object]) ctx.debug(`------------------------------------`) ctx.apiObject = apiObject[object] } else { ctx.error(`API接口${object}不存在`) return } await next() }

RestfulAPIMethods

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 let RestfulAPIMethods = {} let Methods = [‘List‘, ‘Get‘, ‘Post‘, ‘Replace‘, ‘Patch‘, ‘Delete‘, ‘Related‘, ‘AddRelated‘, ‘DelRelated‘] for (let i = 0; i < Methods.length; i++) { let v = Methods[i] RestfulAPIMethods[v] = async function (ctx, next) { let apiObject = ctx.apiObject || ctx.apiRoot if (!apiObject) { ctx.error (‘API裝載異常‘) return } let object = ctx.params.object if (apiObject[object] && apiObject[object].isCollection) { ctx.debug(` --- Restful API [${v}] 調用--- `) if (typeof apiObject[object][v] == ‘function‘) { ctx.state.data = await apiObject[object][v](ctx) ctx.debug(‘路由結束‘) return //ctx.debug(ctx.state.data) } else { ctx.error(`對象${object}不存在操作${v}`) return } } ctx.debug(` --- 當前對象${object}並不是可訪問對象 --- `) await next() } }

需要註意的點

1、koa-router的調用順序
2、涉及到async註意next()需要加await

淺談KOA2 Restful方式路由初探