gaialun

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

最近想利用React + Node开发一个前后端分离的个人网站,其中服务端首选了express框架+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()方法的作用就是:

  1. 获取到src文件夹下的controller文件夹下的每一个文件名

  2. 通过const xx = require(path)的方式引入controller文件夹下的文件中导出的类对象

  3. 实例化xx类,在这个过程中,就会触发@Controller装饰器,将当前类中的路由存到同一个数组中

  4. 循环完每一个路由文件后,再将数组中的路由遍历一遍,注册到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中。

 

对于以上方法,若有觉得方法有不足之处或是值得改进的地方,又或者有更好的方式,欢迎随时指出。各位的批评与指点就是在下进步的良药。

posted on 2022-04-22 21:07  能倒杯茶吗?  阅读(565)  评论(0)    收藏  举报