浅谈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 相关文章推荐
另类调用flash无须激活的方法
Dec 27 Javascript
用YUI做了个标签浏览效果
Feb 20 Javascript
javascript 关闭IE6、IE7
Jun 01 Javascript
jQuery焦点图切换特效插件封装实例
Aug 18 Javascript
jQuery对html元素取值与赋值的方法
Nov 20 Javascript
jQuery html()方法使用不了无法显示内容的问题
Aug 06 Javascript
BootStrap Fileinput的使用教程
Dec 30 Javascript
Linux CentOS系统下安装node.js与express的方法
Apr 01 Javascript
Vue.JS实现垂直方向展开、收缩不定高度模块的JS组件
Jun 19 Javascript
微信小程序scroll-view仿拼多多横向滑动滚动条
Apr 21 Javascript
VUE+node(express)实现前后端分离
Oct 13 Javascript
理解JavaScript中的对象
Aug 25 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最常用的ini函数分析 针对PHP.ini配置文件
2010/04/22 PHP
php 常用类汇总 推荐收藏
2010/05/13 PHP
php使用curl访问https示例分享
2014/01/17 PHP
php生成唯一的订单函数分享
2015/02/02 PHP
php计算一个文件大小的方法
2015/03/30 PHP
laravel Task Scheduling(任务调度)在windows下的使用详解
2019/10/22 PHP
JS多物体 任意值 链式 缓冲运动
2012/08/10 Javascript
JavaScript Math.ceil() 函数使用介绍
2013/12/11 Javascript
JavaScript获取各大浏览器信息图示
2015/11/20 Javascript
JavaScript的Ext JS框架中的GridPanel组件使用指南
2016/05/21 Javascript
JavaScript中push(),join() 函数 实例详解
2016/09/06 Javascript
JS中使用mailto实现将用户在网页中输入的内容传递到本地邮件客户端
2016/10/08 Javascript
SelecT下拉框选中和取值的解决方法
2016/11/22 Javascript
jQuery实现用户输入自动完成功能
2017/02/13 Javascript
详解React Native开源时间日期选择器组件(react-native-datetime)
2017/09/13 Javascript
基于twbsPagination.js分页插件使用心得(分享)
2017/10/21 Javascript
深入剖析Express cookie-parser中间件实现示例
2018/02/01 Javascript
js 实现在2d平面上画8的方法
2018/10/10 Javascript
mock.js实现模拟生成假数据功能示例
2019/01/15 Javascript
小程序click-scroll组件设计
2019/06/18 Javascript
JS实现点击发送验证码 xx秒后重新发送功能
2019/07/30 Javascript
微信小程序通过websocket实时语音识别的实现代码
2020/08/19 Javascript
[10:24]郎朗助力完美“圣”典,天籁交织奏响序曲
2016/12/18 DOTA
[01:21:36]CHAOS vs Alliacne 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/16 DOTA
在Python中使用zlib模块进行数据压缩的教程
2015/06/26 Python
关于PyTorch源码解读之torchvision.models
2019/08/17 Python
Python matplotlib生成图片背景透明的示例代码
2019/08/30 Python
在主流系统之上安装Pygame的方法
2020/05/20 Python
Python中使用aiohttp模拟服务器出现错误问题及解决方法
2020/10/31 Python
Python远程linux执行命令实现
2020/11/11 Python
在vscode中启动conda虚拟环境的思路详解
2020/12/25 Python
2014年综治宣传月活动总结
2014/04/28 职场文书
政府信息公开实施方案
2014/05/09 职场文书
文明工地标语
2014/06/16 职场文书
售后服务质量承诺书
2015/04/29 职场文书
Vue3 Composition API的使用简介
2021/03/29 Vue.js