前言最近考虑将服务器资源整合一下,作为多端调用的API看到Restful标准和ORM眼前一亮,但是找了不少版本路由写的都比较麻烦,于是自己折腾了半天API库结构考虑到全部对象置于顶层将会造成对象名越来长,同时不便于维护,故采取部分的分层结构如workflow模块内的prototypes,instances等等,分层的深度定义为层级可访问的对象集合(collection)的属性满足Restful设计 – workflow(category) – prototypes(collection) – [method] … – [method] … – instances(collection) – users(collection) –[method] List #get :object/ –[method] Instance #get :object/:id – … – …RESTFUL API 接口将Restful API接口进行标准化命名.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对象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 1const DefinedRouterDepth = 2let 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对象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()}RestfulAPIMethodslet 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.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