koa ts装饰器应用

201 阅读2分钟

1.改造koa route修饰注册

1.实现原理

  • 1.通过实现扫描文件夹自动注册路由 /src/routes/user.ts
  • 2.对应的user.ts类使用 @get @post 修饰方法,完成路由注册
//修饰路由的原始写法 
const router = new KoaRouter()
 export const get = (path:String) => {
     return (target,property) => {
         router['get'](path,target[property]) //target[property]代表当前class 对应的方法
         //router['get'] 相当于 router.get() 调用
     }
 }

2.代码实现

//index.js
import * as Koa from 'koa'
import * as bodify from 'koa-body';
import * as serve from 'koa-static';
import * as timing from 'koa-xtime';
import * as Router from 'koa-router';
import {load} from './utils/route-decorors'
const app = new Koa()
app.use(
    bodify({
        multipart:true,
        strict:false
    })
) 
import {resolve} from 'path' 

//普通路由的写法
// const router = new Router()
// router.get('/abc',ctx => {
//   ctx.body = 'abc'
// })
//通过加载文件夹加载的方式
const router = load(resolve(__dirname, './routes'))
app.use(router.routes())

app.listen(3000 ,() => {
    console.log('服务器启动成功。。')
})


//src/utils/route-decorors.ts
import * as glob from 'glob';
import * as Koa from 'koa';
import * as KoaRouter from 'koa-router'; 

const router = new KoaRouter()

//通过柯里化 传入router 解决引用透明原则
export const method = (router:KoaRouter) =>  methodName => (path:String) => {
    return (target,property) => {
        router[methodName](path,target[property])
    }
}
const routeDecorotes = method(router)
export const get = routeDecorotes('get')
export const post = routeDecorotes('post')

//遍历文件夹下所有class
export const load = (folder:String) :KoaRouter => {
    const extname = '.{js,ts}'
    glob.sync(require('path').join(folder, `./**/*${extname}`)).forEach((item) => require(item))
    return router //返回路由
} 

//src/routes/user.ts
import * as Koa from 'koa'
import {get,post} from '../utils/route-decorors'
const users = [{ name: 'tom', age: 20 }]
export default class User {
    @get('/users')
    public list(ctx: Koa.Context) {
        ctx.body = {
            ok: 1,
            data : users
        }
    }
    @post('/users')
    public add(ctx: Koa.Context) {
        users.push(ctx.request.body)
        ctx.body = { ok: 1}
    }
}

2.添加校验

//利用koa路由支持中间件特性
//src/utils/route-decorors.ts
type RouteOptions = {
    prefix?: string;
    middlewares?: Array<Koa.Middleware>
}

export const method = (router:KoaRouter) =>  methodName => (path:String,options: RouteOptions = {}) => {
    return (target, property: string) => { 
            const middlewares = [] 
            if (target.middlewares) {//中间件优先执行 //再执行路由
                middlewares.push(...target.middlewares)
            } 
            middlewares.push(target[property])
            const url = options.prefix ? options.prefix + path : path
            router[methodName](url, ...middlewares) //这里传入的是一个中间件数组,而不是一个方法
    }
}


//src/routes/user.ts
export default class User {
    @get('/users'
    )
    public list(ctx: Koa.Context) {
        ctx.body = {
            ok: 1,
            data : users
        }
    }
    //新增校验中间件代码
    @post('/users'
    , {
        middlewares: [
            async function validation(ctx: Koa.Context, next: () => Promise<any>) {
                // 用户名必填
                const name = ctx.request.body.name
                if (!name) {
                    throw "请输入用户名";
                }
                await next();
            }
        ]
    })
    public add(ctx: Koa.Context) {
        users.push(ctx.request.body)
        ctx.body = { ok: 2}
    }
}

3.添加类的全局校验

//利用  process.nextTick(() => {})//此方法是为了延迟执行路由注册,由于方法的中间件优先与类的中间件执行
//src/utils/route-decorors.ts
export const method = (router:KoaRouter) =>  methodName => (path:String,options: RouteOptions = {}) => {
    return (target, property: string) => { 
            process.nextTick(() => { //此方法是为了延迟执行路由注册,由于方法的中间件优先与类的中间件执行
                const middlewares = []
                if (options.middlewares) {//这里获取 class的定义的中间件
                    middlewares.push(...options.middlewares)
                } 
                if (target.middlewares) {
                    middlewares.push(...target.middlewares)
                } 
                middlewares.push(target[property])
                const url = options.prefix ? options.prefix + path : path
                router[methodName](url, ...middlewares) 
            })
    }
}

//定义类使用的中间件
export const middlewares = (middlewares: Koa.Middleware[]) => {
    return function (target) {//直接把middleware添加到原型上,在路由注册的时候使用
        target.prototype.middlewares = middlewares
    }
}

4.数据层自动加载

npm i -s sequelize-typescript@0.6.11
npm i -s types/sequelize@4.28.9
//通过sequelize-typescript 写好的装饰器自动加载对象
 
// model/user.ts
import { Table, Column, Model, DataType } from 'sequelize-typescript';

@Table({modelName: 'users'})
export default class User extends Model<User> {
    @Column({
        primaryKey: true,
        autoIncrement: true,
        type: DataType.INTEGER,
    })
    public id: number;

    @Column(DataType.CHAR)
    public name: string;
}

//src/index.ts
import { Sequelize } from 'sequelize-typescript';
//自动扫描model文件夹下的类
const database = new Sequelize({    
    port:3306,
    database:'xxxx',
    username:'admin',
    host: '81.xxx.xx.x',
    password:'xxxx',
    dialect:'mysql',
    modelPaths: [`${__dirname}/model`], 
}); 
database.sync({force: true})

//routes/user.ts
import modelUser from '../model/user'//引入数据库访问层
export default class User {
    @get('/users'
    )
    public async list(ctx: Koa.Context) { 
        const users = await model.findAll()//访问数据库查询用户信息
        console.log("数据库查询内容",users);
        ctx.body = {
            ok: 1,
            data : users
        }
    }
}

5.使用parameter 校验body

//src/utils/route-decorors.ts
import * as Parameter from 'Parameter'
const validateRule = paramPart => rule => {
    return function (target, name, descriptor) {
        const oldValue = descriptor.value
        descriptor.value = function () {
            const ctx = arguments[0]
            const p = new Parameter()//通过 parameter工具类校验
            const data = ctx[paramPart]
            const errors = p.validate(rule, data)
            console.log('error',errors)
            if (errors) throw new Error(JSON.stringify(errors))
            return oldValue.apply(null, arguments);
        }
        return descriptor;
    }
}

export const querystring = validateRule('query')//这里只处理url参数

//routes/user.ts
//请求get 的时候校验给的age参数是否合法
export default class User {
    @get('/users' )
    @querystring({
        age: { type: 'int', required: false, max: 200, convertType: 'int' },
    })
    public async list(ctx: Koa.Context) { 
        ctx.body = { ok: 1, data: users }
        console.log("数据库查询内容",users);
        ctx.body = {
            ok: 1,
            data : users
        }
    }   
 }