通过装饰器实现项目路由功能
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')
}
}