浅谈KOA2 Restful方式路由初探

前言

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

猜你喜欢

转载自www.cnblogs.com/lorelei123/p/10688832.html