装饰器

264 阅读3分钟

通过装饰器实现项目路由功能

package.json

{

  "scripts": {
    "dev:build": "tsc -w",
    "dev:start": "nodemon node ./build/index.js",
    "dev": "tsc && concurrently npm:dev:*"
  },
  "nodemonConfig": {
    "ignore": [
      "data/*"
    ]
  },
  "devDependencies": {
    "@types/express": "^4.17.12",
    "concurrently": "^6.2.0",
    "nodemon": "^2.0.7",
    "ts-node": "^10.0.0"
  },
  "dependencies": {
    "express": "^4.17.1",
    "reflect-metadata": "^0.1.13",
    "typescript": "^4.3.2"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "outDir": "./build",                        /* Redirect output structure to the directory. */
    "rootDir": "./src",                       /* Specify the root directory of input files. Use 

    "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
    "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */

  }
}

主入口index.ts

import express from 'express'
import { router } from './controller/decorator'
import './controller/loginController'
const app = express()

app.use(router)

app.listen(7001, () => {
  console.log('server is running')
})

decorator

//src/controller/decorator.ts
import { Router } from 'express'
export const router = Router()

export function controller (target: any) {
  console.log('target', target) // [Function: LoginController]
  console.log('target.prototype', target.prototype) // LoginController { home: [Function], login: [Function] }
  for (let key in target.prototype) {
    console.log('key', key) // home,login

    const path = Reflect.getMetadata('path', target.prototype, key)
    const handler = target.prototype[key]
    console.log('handler', handler) // home (req: Request, res: Response) {res.send('home')}
    if (path) {
      router.get(path, handler)
    }
  }
}

export function get (path: string) {
  return function (target: any, key: string) {
    console.log('path', path) // /
    console.log('target', target) // LoginController { home: [Function], login: [Function] }
    console.log('key', key) // home
    Reflect.defineMetadata('path', path, target, key)
  }
}

controller

// src/controller/loginController.ts

import 'reflect-metadata'
import { controller, get } from './decorator'
import { Request, Response } from 'express'

@controller
class LoginController {
  @get('/')
  home (req: Request, res: Response) {
    res.send('home--')
  }

  @get('/login')
  login (req: Request, res: Response) {
    res.send('login')
  }
}

等同于

router.get('/',(req: Request, res: Response)=> {
   res.send('home--')
})

router.get('/login',(req: Request, res: Response)=> {
   res.send('login')
})

不同请求方法的装饰器生成

decorator

// src/controller/decorator.ts
import { Router, RequestHandler } from 'express'
export const router = Router()

enum Method {
  get = 'get',
  post = 'post'
}

export function controller (target: any) {
  console.log('target', target) // [Function: LoginController]
  console.log('target.prototype', target.prototype) // LoginController { home: [Function], login: [Function] }
  for (let key in target.prototype) {
    console.log('key', key) // home,login

    const path = Reflect.getMetadata('path', target.prototype, key)
    const method: Method = Reflect.getMetadata('method', target.prototype, key)
    const handler = target.prototype[key]
  
    console.log('handler', handler) // home (req: Request, res: Response) {res.send('home')}
    if (path && method && handler) {
       router[method](path, handler)
    }
  }
}


export function getRequestDecorator (type: string) {
  return function (path: string) {
    return function (target: any, key: string) {
      Reflect.defineMetadata('path', path, target, key)
      Reflect.defineMetadata('method', type, target, key)
    }
  }
}

export const get = getRequestDecorator('get')
export const post = getRequestDecorator('post')

controller

// src/controller/loginController.ts

import 'reflect-metadata'
import { controller, get, post } from './decorator'
import { Request, Response, NextFunction } from 'express'

@controller
class LoginController {
  @get('/')
  home (req: Request, res: Response) {
    res.send(`
    <html>
    <body>
      <form method="post" action="/login">
        <input type="password" name="password" />
        <button>登陆</button>
      </form>
    </body>
  </html>
  `)
  }

  @post('/login')
  login (req: Request, res: Response) {
    res.send('login')
  }
}

中间件装饰器的编写

decorator

// src/controller/decorator.ts

import { Router, RequestHandler } from 'express'
export const router = Router()

enum Method {
  get = 'get',
  post = 'post'
}

export function controller (target: any) {
  console.log('target', target) // [Function: LoginController]
  console.log('target.prototype', target.prototype) // LoginController { home: [Function], login: [Function] }
  for (let key in target.prototype) {
    console.log('key', key) // home,login

    const path = Reflect.getMetadata('path', target.prototype, key)
    const method: Method = Reflect.getMetadata('method', target.prototype, key)
    const handler = target.prototype[key]
    const middleware = Reflect.getMetadata('middleware', target.prototype, key)
    console.log('handler', handler) // home (req: Request, res: Response) {res.send('home')}
    if (path && method && handler) {
      if (middleware) {
        router[method](path, middleware, handler)
      } else {
        router[method](path, handler)
      }
    }
  }
}

export function use (middleware: RequestHandler) {
  return function (target: any, key: string) {
    Reflect.defineMetadata('middleware', middleware, target, key)
  }
}

export function getRequestDecorator (type: string) {
  return function (path: string) {
    return function (target: any, key: string) {
      Reflect.defineMetadata('path', path, target, key)
      Reflect.defineMetadata('method', type, target, key)
    }
  }
}

export const get = getRequestDecorator('get')
export const post = getRequestDecorator('post')

controller

// src/controller/loginController.ts
import 'reflect-metadata'
import { controller, get, post, use } from './decorator'
import { Request, Response, NextFunction } from 'express'

const checkLogin = (req: Request, res: Response, next: NextFunction) => {
  const isLogin = false
  if (isLogin) {
    next()
  } else {
    res.json('请先登录')
  }
}

@controller
class LoginController {
  @get('/')
  home (req: Request, res: Response) {
    res.send(`
    <html>
    <body>
      <form method="post" action="/login">
        <input type="password" name="password" />
        <button>登陆</button>
      </form>
    </body>
  </html>
  `)
  }

  @post('/login')
  login (req: Request, res: Response) {
    res.send('login')
  }

  @get('/getData')
  @use(checkLogin)
  getData (req: Request, res: Response) {
    res.send('getData')
  }
}

在一个方法上使用多个装饰器

decorator

import { Router, RequestHandler } from 'express'
export const router = Router()

enum Method {
  get = 'get',
  post = 'post'
}

export function controller (target: any) {
  console.log('target', target) // [Function: LoginController]
  console.log('target.prototype', target.prototype) // LoginController { home: [Function], login: [Function] }
  for (let key in target.prototype) {
    console.log('key', key) // home,login

    const path = Reflect.getMetadata('path', target.prototype, key)
    const method: Method = Reflect.getMetadata('method', target.prototype, key)
    const handler = target.prototype[key]
    const middlewares: RequestHandler[] = Reflect.getMetadata(
      'middlewares',
      target.prototype,
      key
    )
    console.log('handler', handler) // home (req: Request, res: Response) {res.send('home')}
    if (path && method && handler) {
      if (middlewares && middlewares.length) {
        router[method](path, ...middlewares, handler)
      } else {
        router[method](path, handler)
      }
    }
  }
}

export function use (middleware: RequestHandler) {
  return function (target: any, key: string) {
    const originMiddlewares =
      Reflect.getMetadata('middlewares', target, key) || []
    originMiddlewares.push(middleware)
    Reflect.defineMetadata('middlewares', originMiddlewares, target, key)
  }
}

export function getRequestDecorator (type: string) {
  return function (path: string) {
    return function (target: any, key: string) {
      Reflect.defineMetadata('path', path, target, key)
      Reflect.defineMetadata('method', type, target, key)
    }
  }
}

export const get = getRequestDecorator('get')
export const post = getRequestDecorator('post')

controlelr

import 'reflect-metadata'
import { controller, get, post, use } from './decorator'
import { Request, Response, NextFunction } from 'express'

const checkLogin = (req: Request, res: Response, next: NextFunction) => {
  console.log('checkLogin-----------')
  const isLogin = true
  if (isLogin) {
    next()
  } else {
    res.json('请先登录')
  }
}

const test = (req: Request, res: Response, next: NextFunction) => {
  console.log('test-----------')
  next()
}

@controller
class LoginController {
  @get('/')
  home (req: Request, res: Response) {
    res.send(`
    <html>
    <body>
      <form method="post" action="/login">
        <input type="password" name="password" />
        <button>登陆</button>
      </form>
    </body>
  </html>
  `)
  }

  @post('/login')
  login (req: Request, res: Response) {
    res.send('login')
  }

  @get('/getData')
  @use(checkLogin)
  @use(test)
  getData (req: Request, res: Response) {
    res.send('getData')
  }
}