浅谈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 相关文章推荐
Jquery中对数组的操作代码
Aug 12 Javascript
jquery应该如何来设置改变按钮input的onclick事件
Dec 10 Javascript
js键盘上下左右键怎么触发function(实例讲解)
Dec 14 Javascript
js浮点数精确计算(加、减、乘、除)
Dec 26 Javascript
JS实现图片平面旋转的方法
Mar 01 Javascript
实例解析jQuery工具函数
Dec 01 Javascript
详解webpack中的hash、chunkhash、contenthash区别
Jan 05 Javascript
JavaScript设计模式之构造函数模式实例教程
Jul 02 Javascript
JS获取子节点、父节点和兄弟节点的方法实例总结
Jul 06 Javascript
记一次webapck4 配置文件无效的解决历程
Sep 19 Javascript
iview的table组件自带的过滤器实现
Jul 12 Javascript
vue项目使用高德地图的定位及关键字搜索功能的实例代码(踩坑经验)
Mar 07 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开发入门教程之面向对象
2006/12/05 PHP
rrmdir php中递归删除目录及目录下的文件
2011/05/15 PHP
基于PHPExcel的常用方法总结
2013/06/13 PHP
php+js iframe实现上传头像界面无跳转
2014/04/29 PHP
dedecms集成财付通支付接口
2014/12/28 PHP
PHP实现的防止跨站和xss攻击代码【来自阿里云】
2018/01/29 PHP
ThinkPHP5与单元测试PHPUnit使用详解
2020/02/23 PHP
js 鼠标拖动对象 可让任何div实现拖动效果
2009/11/09 Javascript
ASP.NET jQuery 实例15 通过控件CustomValidator验证CheckBoxList
2012/02/03 Javascript
formvalidator验证插件中有关ajax验证问题
2013/01/04 Javascript
js实现分享到随页面滚动而滑动效果的方法
2015/04/10 Javascript
使用CSS+JavaScript或纯js实现半透明遮罩效果的实例分享
2016/05/09 Javascript
AngularJS服务service用法总结
2016/12/13 Javascript
vue.js学习之递归组件
2016/12/13 Javascript
JavaScript中从setTimeout与setInterval到AJAX异步
2017/02/13 Javascript
JavaScript canvas实现围绕旋转动画
2017/11/18 Javascript
boostrap模态框二次弹出清空原有内容的方法
2018/08/10 Javascript
jQuery 同时获取多个标签的指定内容并储存为数组
2018/11/20 jQuery
Vue动态路由缓存不相互影响的解决办法
2019/02/19 Javascript
你可能不知道的CORS跨域资源共享
2019/03/13 Javascript
webpack打包优化的几个方法总结
2020/02/10 Javascript
[01:45]2014DOTA2 TI预选赛预选赛 战前探营!
2014/05/21 DOTA
[01:03:37]Secret vs VGJ.S Supermajor小组赛C组 BO3 第二场 6.3
2018/06/04 DOTA
使用matlab或python将txt文件转为excel表格
2019/11/01 Python
Flask 上传自定义头像的实例详解
2020/01/09 Python
如何使用selenium和requests组合实现登录页面
2020/02/03 Python
Python 从attribute到property详解
2020/03/05 Python
Python HTMLTestRunner测试报告view按钮失效解决方案
2020/05/25 Python
CSS3选择器新增问题的实现
2021/01/21 HTML / CSS
浅析移动设备HTML5页面布局
2015/12/01 HTML / CSS
处理textarea中的换行和空格
2019/12/12 HTML / CSS
机械设计及其自动化专业推荐信
2013/10/31 职场文书
索桥的故事教学反思
2014/02/06 职场文书
委托公证书样本
2015/01/23 职场文书
2016年寒假社会实践活动总结
2015/03/27 职场文书
海上钢琴师的观后感
2015/06/11 职场文书