浅谈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 相关文章推荐
fckeditor 获取文本框值的实现代码
Feb 09 Javascript
基于jquery实现漂亮的动态信息提示效果
Aug 02 Javascript
Extjs NumberField后面加单位实现思路
Jul 30 Javascript
jQuery实现字符串按指定长度加入特定内容的方法
Mar 11 Javascript
原生JS实现拖拽图片效果
Aug 27 Javascript
Bootstrap每天必学之轮播(Carousel)插件
Apr 25 Javascript
json传值以及ajax接收详解
May 24 Javascript
利用JS实现文字的聚合动画效果
Jan 22 Javascript
Bootstrap实现基于carousel.js框架的轮播图效果
May 02 Javascript
微信小程序倒计时功能实现代码
Nov 09 Javascript
jQuery实现基本淡入淡出效果的方法详解
Sep 05 jQuery
js实现课堂随机点名系统
Nov 21 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不允许用户提交空表单(php空值判断)
2013/11/12 PHP
php快递单号查询接口使用示例
2014/05/05 PHP
php向js函数传参的几种方法
2014/08/10 PHP
php实现简单爬虫的开发
2016/03/28 PHP
jquery一般方法介绍 入门参考
2011/06/21 Javascript
javascript函数定义的几种区别小结
2014/01/06 Javascript
jquery判断复选框是否选中进行答题提示特效
2015/12/10 Javascript
javascript每日必学之循环
2016/02/19 Javascript
关于在Servelet中如何获取当前时间的操作方法
2016/06/28 Javascript
bootstrap Validator 模态框、jsp、表单验证 Ajax提交功能
2017/02/17 Javascript
详解React 16 中的异常处理
2017/07/28 Javascript
完美解决手机浏览器顶部下拉出现网页源或刷新的问题
2017/11/30 Javascript
浅谈vue的几种绑定变量的值 防止其改变的方法
2018/03/01 Javascript
vue路由--网站导航功能详解
2019/03/29 Javascript
微信小程序button标签open-type属性原理解析
2020/01/21 Javascript
Element Carousel 走马灯的具体实现
2020/07/26 Javascript
Echarts在Taro微信小程序开发中的踩坑记录
2020/11/09 Javascript
Python深入学习之对象的属性
2014/08/31 Python
深入理解Python中range和xrange的区别
2017/11/26 Python
Python实现的读写json文件功能示例
2018/06/05 Python
django admin 添加自定义链接方式
2020/03/11 Python
基于Python爬取fofa网页端数据过程解析
2020/07/13 Python
Python 如何测试文件是否存在
2020/07/31 Python
Python二元算术运算常用方法解析
2020/09/15 Python
基于Python爬取股票数据过程详解
2020/10/21 Python
Numpy中np.random.rand()和np.random.randn() 用法和区别详解
2020/10/23 Python
HTML5 客户端数据库简易使用:IndexedDB
2019/12/19 HTML / CSS
Hashtable 添加内容的方式有哪几种,有什么区别?
2012/04/08 面试题
《纸船和风筝》教学反思
2014/02/15 职场文书
《鸟的天堂》教学反思
2014/02/27 职场文书
报关员个人职业生涯规划书
2014/03/12 职场文书
社区活动策划方案
2014/08/21 职场文书
资源环境与城乡规划管理专业自荐书
2014/09/26 职场文书
党员组织生活会发言材料
2014/10/17 职场文书
关于Python OS模块常用文件/目录函数详解
2021/07/01 Python
详解Alibaba Java诊断工具Arthas查看Dubbo动态代理类
2022/04/08 Java/Android