KOA2 Restful方式路由初探

最近考慮將服務器資源整合一下,作爲多端調用的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 文件夾下的文件爲模塊
 *///在此我向大家推薦一個前端全棧開發交流圈:619586920 突破技術瓶頸,提升思維能力 
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)
 //在此我向大家推薦一個前端全棧開發交流圈:619586920 突破技術瓶頸,提升思維能力 
  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

結語

感謝您的觀看,如有不足之處,歡迎批評指正。
獲取資料👈👈👈
本次給大家推薦一個免費的學習羣,裏面概括移動應用網站開發,css,html,webpack,vue node angular以及面試資源等。
對web開發技術感興趣的同學,歡迎加入Q羣:👉👉👉619586920,不管你是小白還是大牛我都歡迎,還有大牛整理的一套高效率學習路線和教程與您免費分享,同時每天更新視頻資料。
最後,祝大家早日學有所成,拿到滿意offer,快速升職加薪,走上人生巔峯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章