浅谈KOA2 Restful方式路由初探

前言

最近考虑将服务器资源整合一下,作为多端调用的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 1

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对象

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

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方式路由初探 的全部内容, 来源链接: utcz.com/z/348038.html

回到顶部