持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情
面向过程编程的express接口
// router.js
import { Router } from 'express'
const router = Router()
router.get('/', (req, res, next) => {})
router.get('/logout', (req, res, next) => {})
export default router
// app.js
import express from 'express'
const app = express()
app.use(router)
app.listen('7001', () => {
console.log('listen at 7001')
})
这就是面向过程编程的写法,如果你用过egg.js框架,它是这么写的:
export default class UserController extends Controller {
async login() {}
}
这种写法就是面向对象OOP的写法。现在我们有了typescript,其实typescript的优势不仅仅是类型提示,它的更大的威力在于它完全是一门面向对象编程的语言,所以使用typescript就需要利用它的优势,把面向过程编程改写为面向对象编程。
创建控制器和装饰器
import 'reflect-metadata'
// 在类的装饰器上面获取类方法的元数据
function controller(target: any) {
for (let key in target.prototype) {
console.log('key', Reflect.getMetadata('path', target.prototype, key))
}
}
// 通过方法装饰器给类的方法设置元数据,即path
function get(path: string) {
return function (target: any, key: string) {
Reflect.defineMetadata('path', path, target, key)
}
}
@controller
class LoginController {
constructor() {}
@get('/login')
login() {
...
}
@get('/')
home(req: Request, res: Response) {
...
}
}
export default LoginController
- 我们对类的方法使用装饰器,装饰器的作用是利用
reflect-metadata来给方法添加元数据path,即路由。 - 在类的装饰器controller上,获取方法的元数据path
通过装饰器实现项目路由功能
1. 存储元数据path和method
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(Method.get)
export const post = getRequestDecorator(Method.post)
2. 读取元数据并生成路由
import { Router } from 'express'
export const router = Router()
export function controller(target: any) {
for (let key in target.prototype) {
const path = Reflect.getMetadata('path', target.prototype, key)
const method: Method = Reflect.getMetadata('method', target.prototype, key)
const handle = target.prototype[key]
if (path && method && handle) {
// 生成路由
router[method](path, handle)
}
}
}
3. 执行装饰器
现在装饰器是设置在类上,想让装饰器执行就必须要执行一次,也就是在入口文件引入对应的类即可:
// 执行一遍LoginController文件,就当详单于执行了装饰器,装饰器里面又生成了路由
import './controller/LoginController'
import { router } from './controller/decorator'
app.use(router)
中间件装饰器
现在我们要利用装饰器来绑定中间件:
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)
}
}
使用中间件:
@controller
export class CrowllerController {
@get('/getData')
@use(checkLogin)
@use(test)
getData(req: BodyRequest, res: Response): void {
const analyzer = DellAnalyzer.getInstance()
new Crowller(url, analyzer)
res.json(getResponseData<responseResult.getData>(true))
}
}