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