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對象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()
    }
}

須要注意的點

一、koa-router的調用順序
二、涉及到async注意next()須要加awaitrestful

相關文章
相關標籤/搜索