TypeScript(二):使用TypeScript改进Express路由

base : https://tasaid.com/Blog/20171011233051.html

express路由

首先我们先看一下传统的express路由是如何使用的:

import * as express from 'express';
import { Response,Request } from 'express'

const app = express();

//传统的路由方式
app.get('/',(req:Request,res:Response) => {
    res.send('首页')
})

app.get('/user',(req:Request,res:Response) => {
    res.send('我的')
})

app.listen(3000,() => {
    console.log('Example app listening at http://localhost:3000')
})

我们可以看到,传统的路由就是有点像流水线一样,而且看起来并不是那么好看。上一章我们学到了装饰器和反射的相关知识,那么现在我们需要使用这些知识来改造路由,对他进行一下升级。

改造路由

基于_装饰器和反射_,我们对路由的改造的最终结果应该像下面所示:

class User {
    @httpGet
    @path('/user/login')
    login() {
        return 'user login'
    }

    @httpGet
    @path('/user/exit')
    exit() {
        return 'user logout!!!!'
    }

}

这种方式和传统的路由使用方式相比,这种基于装饰器和反射的路由有一下优势:

  • 将路由抽离处理成来装饰器,整个router函数只需要处理业务逻辑即可
  • 隐藏res、req,路由返回值直接return即可
  • 更加优雅、配置简单明了

装饰器

这里我们需要简单实现两个装饰器:

  • httpMethods:封装http请求方法,如get、post等。用于应对不同的请求方法。
  • path:封装path,用于处理请求路径和请求参数

httpMethods

import 'reflect-metadata'

export const symbolHttpMethodsKey = Symbol("router:httpMethod")

function createMethods(method:string){
    return function httpMethodDecorator(target:any,targetKey:string){
        //注解:注入元数据 --> 请求方法
        Reflect.defineMetadata(symbolHttpMethodsKey,method,target,targetKey);
    }
}

export const httpGet = createMethods('get');
export const httpPost = createMethods('post');

path

import 'reflect-metadata'
import { Response,Request }  from 'express'

export const symbolPathKey = Symbol.for('router:path')

export const path = function(path:string):Function {
    return function(target:any,targetkey:string,descroptior:PropertyDescriptor){
        //注解:注入元数据 --> 请求路径
        Reflect.defineMetadata(symbolPathKey,path,target,targetkey);
        //如果不存在回调函数
        if(!descroptior.value) return 
        //保存原始回调函数
        let oldMethod = descroptior.value;
        //重写回调函数 
        descroptior.value = function(req:Request,res:Response){
            //获取请求参数
            const params = Object.assign({},req.body,req.query);
            //调用回调函数 获取返回值
            let result = oldMethod.call(this,params);
            //给浏览器发送结果
            res.send(result)
        }
    }
}

Router –> Controller

现在我们需要将原有的业务进行抽离,按照业务进行归类,处理成一个个的class,如:

class User {
    @httpGet
    @path('/user/login')
    login() {
        return 'user login'
    }

    @httpGet
    @path('/user/exit')
    exit() {
        return 'user logout!!!!'
    }

    /**
     * 属性装饰器
     * @param v1
     */
    // @httpGet
    // @path('/validate')
    // @validateEmptyStr
    // valid(@required v1: string) {
    //   console.log(v1)
    //   return v1
    // }
}

然后在程序入口文件处,将class里面的方法遍历一遍,将其挂载到app实例上:

export default (app: Router) => {
    let user = new User()
    for (let methodName in user) {
        let method = user[methodName]
        if (typeof method !== 'function') break
        // 得到注解的数据
        let httpMethod = Reflect.getMetadata(
            symbolHttpMethodsKey,
            user,
            methodName
        )
        let path = Reflect.getMetadata(symbolPathKey, user, methodName)

    // app.get('/', () => any)
    //在app实例挂载路由和对应的回调函数
        app[httpMethod](path, method)
    }
}

到这里,我们基于_装饰器和反射_的路由器改造就基本完成了。
下一步就是编译和运行了。

编译和运行

编译

这里我选用的是Gulp进行编译。

const { src,dest,watch,series } = require('gulp')
const del = require('del');
const ts = require('gulp-typescript');
const tsProject = ts.createProject("tsconfig.json");

function clean(cb){
    return del(['dist'],cb)
}

function build(){
    return watch('src/**/*.ts',{ events:'all',delay:500,ignoreInitial:false },function(){
        return src('src/**/*.ts')
        .pipe(tsProject())
        .pipe(dest("dist"))
    })
}

exports.default =  series(clean,build)
  • gulp-typescript 对typescript进行处理,将ts文件处理成js文件
  • 使用del在重新编译之前将上次编译的文件夹删除,类似于rm -rf

然后我们只需要在终端执行npx gulp即可。

运行

在package.json中配置scripts,执行即可。

{
    "name": "ts-router-to-constroller",
    "version": "1.0.0",
    "license": "MIT",
    "main": "dist/app.js",
    "scripts": {
        "dev": "nodemon dist/app.js"
    },
    "dependencies": {
        "@types/express": "^4.17.6",
        "@types/node": "^14.0.1",
        "del": "^5.1.0",
        "express": "^4.17.1",
        "gulp": "^4.0.2",
        "gulp-livereload": "^4.0.2",
        "gulp-nodemon": "^2.5.0",
        "gulp-typescript": "^6.0.0-alpha.1",
        "typescript": "^3.9.2"
    }
}

源码点击这里

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!