浅谈KOA2 Restful方式路由初探


Posted in Javascript onMarch 14, 2019

前言

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js Event对象的5种坐标
Sep 12 Javascript
JavaScript 图像动画的小demo
May 23 Javascript
node.js中的path.sep方法使用说明
Dec 08 Javascript
JavaScript中的toUTCString()方法使用详解
Jun 12 Javascript
js 基础篇必看(点击事件轮播图的简单实现)
Aug 20 Javascript
关于Jquery中的bind(),on()绑定事件方式总结
Oct 26 Javascript
ES6入门教程之Iterator与for...of循环详解
May 17 Javascript
vue后台管理之动态加载路由的方法
Aug 13 Javascript
vuejs+element UI点击编辑表格某一行时获取内容填入表单的示例
Oct 31 Javascript
Three.js中矩阵和向量的使用教程
Mar 19 Javascript
Vuex,iView UI面包屑导航使用扩展详解
Nov 04 Javascript
微信小程序学习总结(五)常见问题实例小结
Jun 04 Javascript
详解微信小程序scroll-view横向滚动的实践踩坑及隐藏其滚动条的实现
Mar 14 #Javascript
详解React项目中碰到的IE问题
Mar 14 #Javascript
Node.js + express实现上传大文件的方法分析【图片、文本文件】
Mar 14 #Javascript
React+Antd+Redux实现待办事件的方法
Mar 14 #Javascript
Node.js + express基本用法教程
Mar 14 #Javascript
Vue渲染过程浅析
Mar 14 #Javascript
详解关于JSON.parse()和JSON.stringify()的性能小测试
Mar 14 #Javascript
You might like
PHP获取网址的顶级域名函数代码
2012/09/24 PHP
用 Composer构建自己的 PHP 框架之构建路由
2014/10/30 PHP
php去除头尾空格的2种方法
2015/03/16 PHP
PHP分页初探 一个最简单的PHP分页代码的简单实现
2016/06/21 PHP
PHP实现上传多图即时显示与即时删除的方法
2017/05/09 PHP
使用Apache的rewrite
2021/03/09 Servers
用正则表达式 动态创建/增加css style script 兼容IE firefox
2009/03/10 Javascript
jQuery 1.5最新版本的改进细节分析
2011/01/19 Javascript
css3元素简单的闪烁效果实现(html5 jquery)
2013/12/28 Javascript
jQuery实现的Div窗口震动特效
2014/06/09 Javascript
JavaScript中Number.MIN_VALUE属性的使用示例
2015/06/04 Javascript
angularjs学习笔记之完整的项目结构
2015/09/26 Javascript
javascript嵌套函数和在函数内调用外部函数的区别分析
2016/01/31 Javascript
分享js粘帖屏幕截图到web页面插件screenshot-paste
2020/08/21 Javascript
结合代码图文讲解JavaScript中的作用域与作用域链
2016/07/05 Javascript
如何检测JavaScript的各种类型
2016/07/30 Javascript
jQuery多选框选择数量限制方法
2017/02/08 Javascript
Bootstrap警告框(Alert)插件使用方法
2017/03/21 Javascript
ES6中Math对象新增的方法实例详解
2017/04/25 Javascript
Vue.js实现图片的随意拖动方法
2018/03/08 Javascript
红黑树的插入详解及Javascript实现方法示例
2018/03/26 Javascript
Vue实现一个无限加载列表功能
2018/11/13 Javascript
vue实现简单全选和反选功能
2020/09/15 Javascript
在vue中通过render函数给子组件设置ref操作
2020/11/17 Vue.js
将Python中的数据存储到系统本地的简单方法
2015/04/11 Python
python用模块zlib压缩与解压字符串和文件的方法
2016/12/16 Python
Python之csv文件从MySQL数据库导入导出的方法
2018/06/21 Python
Django连接数据库并实现读写分离过程解析
2019/11/13 Python
解决pytorch报错:AssertionError: Invalid device id的问题
2020/01/10 Python
python时间序列数据转为timestamp格式的方法
2020/08/03 Python
美国时装品牌:Nautica(诺帝卡)
2016/08/28 全球购物
英国乐购杂货:Tesco Groceries
2018/11/29 全球购物
简述synchronized和java.util.concurrent.locks.Lock的异同
2014/12/08 面试题
趣味游戏活动方案
2014/02/07 职场文书
品牌推广活动策划方案
2014/08/19 职场文书
复活读书笔记
2015/06/29 职场文书