koa使用裝飾器動(dòng)態(tài)創(chuàng)建路由(router)

前言

在node項(xiàng)目,不管是koa/express路由的使用中,我們創(chuàng)建路由一般都是這樣的姿勢(shì)

router.post("/api/test", middleware, handler); // 創(chuàng)建路由

比如:

router.jpg

這里小編推薦一個(gè)福利,更多精彩內(nèi)容請(qǐng)點(diǎn)擊鏈接,點(diǎn)擊這里

我們一般創(chuàng)建路由的handler會(huì)單獨(dú)抽到其它文件也就是所說(shuō)的controller,這樣就會(huì)多了一步編寫(xiě)router的過(guò)程。那么這一步是否可以省略呢?

當(dāng)然可以,本文帶你一步步使用裝飾器統(tǒng)一處理構(gòu)建路由,這樣不用在寫(xiě)完某一個(gè)controller的方法后再進(jìn)行創(chuàng)建router啦,使用裝飾器,我們只需要在某一個(gè)接口方法上添加路由的裝飾就可以進(jìn)行創(chuàng)建router。那么精彩來(lái)啦~

項(xiàng)目結(jié)構(gòu)

項(xiàng)目結(jié)構(gòu).jpg

開(kāi)始啦,一步步教你使用裝飾器構(gòu)建路由嘍!

初始化項(xiàng)目

初始化package.json

新建一個(gè)目錄,用vscode打開(kāi)。執(zhí)行npm init初始化創(chuàng)建package.json

安裝koa項(xiàng)目使用的第三方包:

npm install -S  koa koa-router koa-bodyparser koa-compress
  • koa-router 管理路由
  • koa-bodyparser 讀取post,put數(shù)據(jù)轉(zhuǎn)化對(duì)象格式
  • koa-compress 壓縮請(qǐng)求數(shù)據(jù)提高傳輸速度

配置babel支持ES6 ES7

因?yàn)轫?xiàng)目使用裝飾器配置路由,必須支持ES7語(yǔ)法,才需要配置babel

本文配置的是babel7版本

下面我?guī)Т蠹乙徊讲脚渲胋abel

npm install -D @babel/core 
npm install -D @babel/preset-env
npm install -D @babel/plugin-proposal-decorators
npm install -D @babel/register

babel7版本 使用的是@babel,7以下是babel-xxx 這點(diǎn)很容易區(qū)分

下面介紹一下這些包的功能

  • @babel/core babel 核心代碼
  • @babel/preset-env 編譯新版的語(yǔ)法 如:箭頭函數(shù),但是并不轉(zhuǎn)換新版api 如:Array.include 轉(zhuǎn)換新版api及兼容瀏覽器之間的差異(兼容ie)需要 babel-polyfill
  • @babel/plugin-proposal-decorators 解析裝飾器
  • @babel/register 在運(yùn)行時(shí)進(jìn)行即時(shí)編譯,不是進(jìn)行先編譯在運(yùn)行

然后在項(xiàng)目根目錄創(chuàng)建.babelrc文件

配置預(yù)設(shè),插件

{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "node": "current"
      }
    }]
  ],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }]
  ]
}

到此,配置結(jié)束。項(xiàng)目比較簡(jiǎn)單,這些babel配置在本項(xiàng)目已經(jīng)足夠使用,接下來(lái)就可以愉快的使用es6,裝飾器新語(yǔ)法了

創(chuàng)建node服務(wù)

新建index.js,app.js啟動(dòng)一個(gè)服務(wù)

index.js 主要是進(jìn)行babel注冊(cè),以及啟動(dòng)服務(wù)的中介文件。
為什么會(huì)這樣呢?在這個(gè)文件進(jìn)行使用動(dòng)態(tài)編譯,但是編譯的時(shí)候是不會(huì)編譯index.js文件的,所以,這個(gè)文件還是需要使用e5的舊語(yǔ)法

require("@babel/register")
require("./app")

app.js才會(huì)去真正啟動(dòng)node服務(wù)

import Koa from "koa"
import bodyparser from "koa-bodyparser"
import compress from "koa-compress"

const app = new Koa();
app.use(compress());
app.use(bodyparser());


const PORT = 8081;
app.listen(PORT,()=>{
    console.log(`啟動(dòng)成功! env=${process.env.NODE_ENV}`)
    console.log(`Listening at http://localhost:${PORT}`)
})

配置運(yùn)行命令,啟動(dòng)服務(wù)

然后需要添加package.json的執(zhí)行命令

"dev":"cross-env NODE_ENV=development nodemon ./index.js"

這里用到了兩個(gè)node開(kāi)發(fā)中常用的兩個(gè)插件cross-envnodemon其主要用途如下

安裝npm install -D cross-env nodemon,安裝到devDependencies

  • cross-env 給項(xiàng)目添加環(huán)境變量區(qū)分是開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境,一般在執(zhí)行npm命令前添加環(huán)境變量通過(guò)

這個(gè)地方添加了NODE_ENV變量,在程序中可以通過(guò)process.env.NODE_ENV獲取執(zhí)行命令添加的變量值

  • nodemon 可以替代node啟動(dòng)服務(wù),只是nodemon功能豐富,可以監(jiān)聽(tīng)代碼的更改重新啟動(dòng)項(xiàng)目

下面執(zhí)行npm run dev可以看到服務(wù)啟動(dòng)成功

1565227107(1).jpg

添加路由裝飾器

在項(xiàng)目根目錄新建common目錄,然后在common目錄下新建decorator目錄,在這個(gè)目錄新建index.js

no bi bi ,上代碼:

/**
 * 請(qǐng)求方法
 */
export const RequestMethod = {
    "GET":"get",
    "POST": "post",
    "PUT": "pust",
    "DELETE": "delete",
    "OPTION": "option",
    "PATCH": "patch"
}

/**
 * 定義注冊(cè)的路由數(shù)組
 */
export const controllers = [];

/**
 * 給controller添加裝飾
 * @param {*} path 
 */
export function Controller(path=""){
    return function(target){
        // 給controller類(lèi)添加路由前綴
        console.log(target)
        target.prefix = path;
    }
}

/**
 * 給controller類(lèi)的方法添加裝飾
 * url 可選
 * method 請(qǐng)求方法
 * middleware 中間件
 */
export function RequestMapping({url="",method="",middleware=[]}){
    return function(target,name,descriptor){
        let path = "";
        // 判斷有沒(méi)有定義url
        if(!url){
            // 取方法名作為路徑
            path = `/${name}`;
        }else{
            // 自己定義的url
            path = url;
        }
        // 創(chuàng)建router需要的數(shù)據(jù) url,method,middleware(可以沒(méi)有),最終執(zhí)行的方法,裝飾器隊(duì)對(duì)象的構(gòu)造函數(shù)
        const item = {
            url:path,
            method:method,
            middleware:middleware,
            handler:target[name],
            constructor:target.constructor,
        };
        controllers.push(item);
    }
}

路由的統(tǒng)一創(chuàng)建

創(chuàng)建router文件夾,再繼續(xù)創(chuàng)建index.js文件

no bi bi ,上代碼:

此文件是統(tǒng)一創(chuàng)建路由的入口文件,需要傳入koa實(shí)例和koa-router實(shí)例進(jìn)行創(chuàng)建路由和路由裝箱

import { controllers } from "./../common/decorator"
/**
 * 初始化路由
 */
export default (app,router) => {
    controllers.forEach((item) => {
        // 獲取每個(gè)路由的前綴
        const prefix = item.constructor.prefix; 
        let url = item.url;
        if(prefix) url = `${prefix}${url}`; // 組合真正鏈接
        console.log(item.method,url); // 打印請(qǐng)求的路由method,url
        router[item.method](url, ...item.middleware, item.handler); // 創(chuàng)建路由
    });
    app.use(router.routes()).use(router.allowedMethods()) // 路由裝箱
}

然后在 app.js進(jìn)行初始化路由:

initRoutes(app,router)

使用

創(chuàng)建controler/index.js , middleware/index.js

middleware/index.js的測(cè)試代碼

export default async (ctx,next)=>{
    console.log("middleware")
    await next();
}

controler/index.js的測(cè)試代碼:




import {RequestMethod,Controller,RequestMapping } from "./../common/decorator/index"
import TestFun from './../middleware/index'

// 添加Controller前綴
@Controller("/api/test")
export default class TestController {

    // 基本面使用 /api/test/login
    @RequestMapping({
        url:"/login",
        method:RequestMethod.GET, // 定義請(qǐng)求方法
    })
    async login(ctx){
        ctx.body = {
            code:0,
            message:"success"
        }
    }

    // 定義有中間件的router  /api/test/test
    @RequestMapping({
        method:RequestMethod.POST, // 定義請(qǐng)求方法
        middleware: [TestFun] // 使用中間件
    })
    async test(ctx){
        ctx.body = {
            code:0,
            message:"success"
        }
    }

    
}

把conttroller/index.js 在 router/index.js導(dǎo)入并導(dǎo)出
export * from "../controller/index"
如果還有其它c(diǎn)ontroller,那么只需要在router/index.js添加就可以了~

重啟服務(wù)

1565241373(1).jpg

使用postman測(cè)試

1565240592(1).jpg
1565240617(1).jpg

ok,搞定~,以后再也不用寫(xiě)router啦,感覺(jué)爽了很多!

源碼請(qǐng)點(diǎn)擊gitHub地址

代碼注釋很詳細(xì),若有什么不理解或者更好的建議,歡迎各位留言交流共同學(xué)習(xí)~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容