KOA2 Restful方式路由初探

公司裁员
• 阅读 1502

前言

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

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
公司裁员
公司裁员
Lv1
归雁横秋,倦客思家。
文章
4
粉丝
0
获赞
0