使用typescript的裝飾器將express的router路由註冊的操作進行簡化處理
1 import express, {Request, Response} from 'express'
2 const app = express();
3 const router = express.Router();
4
5 // 隨便寫一個get請求的介面
6 router.get('/api/login/loginByPhone', (req:Request, res:Response) => {
7 //...
8 })
9 // 在將路由註冊到app中
10 app.use(router)
但如果有N多個路由,不同的路由肯定會按照需求的情況寫在不同的ts檔案中,例如:
先將router放在單獨的一箇中間件檔案中進行匯出,其他路由檔案引入的都是這一個router物件
// middleware/router.ts
const express = require('express')
// 建立路由物件
const router = express.Router()
// 匯出的路由物件專門用於建立不同的路由: router.post('/api/...')
export default router;
然後新建路由
//LoginRouter.ts
import router from './middleware/router'
router.get('/api/login/loginByPhone', (req:Request, res:Response) => {
//...
})
router.post('/api/login/selectxxx', (req:Request, res:Response) => {
//...
})
//...
// 最後匯出LoginRouter
export default LoginRouter;
//UserRouter.ts
import router from './middleware/router'
router.get('/api/user/selectUserInformation', (req:Request, res:Response) => {
//...
})
router.post('/api/user/saveInformation', (req:Request, res:Response) => {
//...
})
// 匯出UserRouter路由
export default UserRouter;
最後再統一引入到一個檔案中進行註冊:
import LoginRouter from './Router/LoginRouter'
import UserRouter from './Router/UserRouter'
import express from 'express'
const app = express()
// 註冊路由
app.use(LoginRouter);
app.use(UserRouter);
但是如果路由檔案特別多的話(假如有100個),那是不是得在一個檔案裡面引入100個路由檔案挨個進行註冊...
import xxx1 from './Router/xxx1'
import xxx2 from './Router/xxx2'
import xxx3 from './Router/xxx3'
//...
import xxx100 from './Router/xxx100'
app.use(xxx1)
app.use(xxx2)
app.use(xxx3)
//...
app.use(xxx100)
這也太麻煩了,那我豈不是成跪著敲程式碼的了麼? 所以必須想到一個偷懶的辦法!經過深思熟慮反覆斟酌,再憑藉著我對Java的一絲經驗,總算是有了一些思路,於是順著思路馬上開寫:
1)首先就是修改路由檔案中匯出的內容,不再匯出router物件,而是匯出一個類
// ./router/LoginRouter.ts
import router from './middle/router';
export default class LoginRouter{
// 類物件中的屬性用來存放router路由
selectInforByPhone = router.post('/api/login/loginByPassWord', (req: Request, resp: Response) => {
//...
})
registerByPhone = router.post('/api/register/registerByPhone', (req: Request, resp: Response) => {
//...
})
}
2)第二步就是利用typescript寫一個@controller裝飾器放在路由類上,裝飾器的功能很簡單,就是獲取到類物件中的路由
// ./decorators/controller.ts
// 專門儲存類物件中路由的陣列
const routerArray:any = [];
function Controller(target: any): void {
// 例項化當前類
let obj = new target();
// 然後將物件中的每一個router變數儲存到routerArray陣列中
for (let route in obj) {
routerArray.push(obj[route]);
}
}
// Controller裝飾器, 儲存路由的陣列
export {Controller, routerArray};
現在的路由類就變成了這樣
import {Controller} from './decorators/controller'
import router from './middleware/router'
// 現在路由類不叫xxxRouter了,改叫xxxController
@Controller // 加上裝飾器
export default class LoginController{
// 類物件中的變數用來存放router路由
selectInforByPhone = router.post('/api/login/loginByPassWord', (req: Request, resp: Response) => {
//...
})
registerByPhone = router.post('/api/register/registerByPhone', (req: Request, resp: Response) => {
//...
})
}
接下來每次例項化這個類時,就會執行裝飾器函式,將類物件中的路由變數存入到routerArray陣列中。然後只需要對陣列進行遍歷,就可以註冊所有的路由。但是相比於最開始的引入 + 註冊,現在好像更麻煩了。。。不僅要引入路由類,還得例項化路由類,最後需要遍歷陣列才能進行註冊。因此接下來才是改變一切的關鍵:
const path = require('path');
const fs = require('fs');
export default class routerConfig {
// 1.1 獲取controller資料夾的絕對路徑
filePath = `${path.resolve()}\\src\\controller`;
registerRouters(app: any) {
return new Promise((resolve, reject) => {
//1.2 讀取到controller資料夾下的檔名
fs.readdir(this.filePath, (err: any, files: any) => {
if(err){
reject('Controller file path error!');
}
files.forEach((file: any) => {
// 2. 引入當前檔案中的類物件
const nowClass = require(this.filePath + "\\" + file);
if(typeof nowClass === 'object'){
// 3. 每次new當前類時,都會觸發controller裝飾器
new nowClass['default']();
}
})
// 4.1 獲取到存放路由的陣列
const { routerArray } = require('../decorators/controller');
//4.2 遍歷陣列,將路由註冊到app中
routerArray.forEach((route: string) => {
app.use(route);
})
resolve(true);
})
})
}
}
上面的類中的registerRouters()方法的作用就是:
-
獲取到src資料夾下的controller資料夾下的每一個檔名
-
通過const xx = require(path)的方式引入controller資料夾下的檔案中匯出的類物件
-
例項化xx類,在這個過程中,就會觸發@Controller裝飾器,將當前類中的路由存到同一個陣列中
-
迴圈完每一個路由檔案後,再將陣列中的路由遍歷一遍,註冊到app中。
我們最後在index.ts中執行這個操作
// index.ts
const express = require('express');
const app = express();
// 引入關鍵的那個類
import RouteConfig from './src/config/routerConfig'
let routerConfig = new RouteConfig();
// 呼叫routerConfig物件下的registerRouters()方法,並將app物件傳入,路由註冊完畢之後再對8888埠進行監聽
routerConfig.registerRouters(app).then(_ => {
app.listen(8888, () => {
console.log('note-station Server running at http://127.0.0.1');
})
}).catch(err => {
console.log(err);
});
測試:
先啟動服務端,通過nodemon執行以下index.ts檔案
然後隨便啟動一個React專案,隨便訪問一下已有的router,看一下注冊是否成功
即將被訪問的router
訪問/api/user/selectUser
再看看服務端控制檯是否有前端傳來的資料
可以看到資料互動完全沒問題,因此路由的註冊肯定也是成功了的!!
總結:
通過@Controller裝飾器和RouterConfig物件的registerRouters()方法對controller資料夾下的類物件進行處理後,我們不需要再每次新建一個router後都要進行路由的引入和註冊操作。後續只需要在新建的xxxController類上加一個@Controller,那麼最後在啟動服務時就會將這個類中的路由自動註冊到app中。
對於以上方法,若有覺得方法有不足之處或是值得改進的地方,又或者有更好的方式,歡迎隨時指出。各位的批評與指點就是在下進步的良藥。