在考究了nodejs以及nestjs后,觉得nestjs用的不是特别爽,于是开始不自量力的写起了这个框架玩,希望能在造轮子的过程中学到更多有趣的东西。
quick-d基于koa,开发ing
牢记非侵入式设计!!!
牢记非侵入式设计!!!
牢记非侵入式设计!!!
可以使用 quick-d 的cli初始化该框架(需用包管理工具安装)
quick-d-cli init
目录
一、控制层装饰器(@Controller)
二、路由装饰器(@Request,@Get、Post、Put、DeleteRequest)
三、参数装饰器(@BodyParam,@Query)
四、全局错误处理 异常解决方案
五、自动装载与容器管理
核心技术
装饰器、反射
一、控制层装饰器
common/Controller.js 文件中
const Controller = (path: string = ''): ClassDecorator => {
return (originClass: Function) => {
const properties = [{
name: '$Quick-D',
default: {}
}, {
name: 'controllers',
default: {}
}, {
name: originClass.name,
default: originClass
}]
let baseObj = global
properties.forEach(property => {
baseObj[property.name] = baseObj[property.name] ?? property.default
baseObj = baseObj[property.name]
})
Reflect.defineMetadata('path', path, originClass)
Reflect.defineMetadata('router', new Router(), originClass)
}
}
二、路由装饰器
目前已有(五种路由装饰器,其余几个基于Request,就只放Request部分的代码了)
const Request = (path: string = '', reqMethods: string[]): MethodDecorator => {
for (let i = 0; i < reqMethods.length; i++) {
reqMethods[i] = reqMethods[i].toLocaleLowerCase()
}
return (target: {}, methodName: string, method: Object) => {
const originClass = target.constructor
let routes = Reflect.getMetadata('routes', originClass)
if (routes === undefined) {
routes = {}
Reflect.defineMetadata('routes', routes, originClass)
}
routes[methodName] = {
path: path !== undefined ?path:`/${methodName}`,
reqMethods: reqMethods !== undefined ?reqMethods:[ 'get' ],
fun: method
}
}
}
三、参数装饰器
const Query = (
alias: string,required: boolean,
defaultVal: Object, type: Object, verification: Function|RegExp
, ...args) => {
let [ target, methodName, index ] = [ alias, required, defaultVal, type, verification, ...args ]
const decorator = (target: Function , methodName: string, index: number) => {
let queries = Reflect.getMetadata('queries', target[methodName])
if (queries === undefined) {
queries = []
Reflect.defineMetadata('queries', queries, target[methodName])
}
queries.push({
name: alias, required, defaultVal, type, index,
checkInformation: getCheckInformation(verification)
})
Reflect.defineMetadata('queries', queries, target[methodName])
}
if (typeof target === 'object') {
alias = undefined
required = undefined
defaultVal = undefined
decorator(target, methodName, index)
return
}
return decorator
}
const BodyParam = (
alias: string, required: boolean,
defaultVal: Object, type: Object, verification: Function|RegExp
, ...args) => {
return (target: Function , methodName: string, index: number) => {
let bodyParams = Reflect.getMetadata('bodyParams', target[methodName])
if (bodyParams === undefined) {
bodyParams = []
Reflect.defineMetadata('bodyParams', bodyParams, target[methodName])
}
bodyParams.push({
name: alias, required, defaultVal, type, index,
checkInformation: getCheckInformation(verification)
})
Reflect.defineMetadata('bodyParams', bodyParams, target[methodName])
}
}
四、全局错误处理 异常解决方案
下面是异常处理装饰器
const ErrorsHandler = (errors: Array<Error>, weight: Number) => {
errors = errors ?? []
weight = weight ?? 0
return (target: Object, methodName: string, method: Function) => {
const properties = [{
name: '$Quick-D',
default: {}
}, {
name: 'errorsHandlers',
default: {}
}, {
name: target.constructor.name,
default: target.constructor
}]
let baseObj = global
properties.forEach(property => {
baseObj[property.name] = baseObj[property.name] ?? property.default
baseObj = baseObj[property.name]
})
let errorsHandlers = Reflect.getMetadata('errorsHandlers', ErrorListener)
if (errorsHandlers === undefined) {
errorsHandlers = []
Reflect.defineMetadata('errorsHandlers', errorsHandlers, ErrorListener)
}
errorsHandlers.push({
target, methodName,
errors, weight
})
Reflect.defineMetadata('errorsHandlers', errorsHandlers, ErrorListener)
}
}
新建一个异常监听器继承与异常监听器基类 并在项目主体中引入
import {
ErrorListener,
ErrorsHandler
} from '~/lib/common/Handler'
import ValueNotDeliveredError from '~/lib/error/ValueNotDeliveredError'
class ServerErrorListener extends ErrorListener {
isLogStack = process.env.NODE_ENV === 'development'
@ErrorsHandler([ Error ], 1)
dealError ([ ctx, error ]) {
console.log('dealError', error)
}
@ErrorsHandler([ ValueNotDeliveredError ], 10)
dealValueNotDeliveredError ([ ctx, error ]) {
console.log('dealValueNotDeliveredError', error)
}
}
支持多个监听器,只要在主文件引入即可(按照权重分配调用顺序,权重相同时按加载顺序加载)
五、自动装载与容器管理
index.js 文件中
const registerApp = (app: Koa) => {
// 添加全局的异常处理
app.use(async (ctx, next) => {
const errorsHandlers = (Reflect
.getMetadata('errorsHandlers', ErrorListener) ?? []).sort((a, b) => {
return a.weight > b.weight ? -1:1
})
try {
await next()
} catch (e) {
// 处理异常...
}
})
// 解析post请求中的body参数
app.use(koaBodyparser())
// 覆盖koa原本的监听事件
const oldListen = app.listen
app.listen = (...args) => {
const controllers = (global?.['$Quick-D'] ?? {
controllers: []
}).controllers
for (const controllerName in controllers) {
const controller = controllers[controllerName]
const instance = new controller()
const {
path, router, routes
} = reflectGetData(controller)
for (const methodName in routes) {
const route = routes[methodName]
route.reqMethods.forEach(reqMethod => {
router[reqMethod](`${path}${route.path}`, async ctx => {
const
instanceArgs = [],
method = instance[methodName],
argNames = method.toString()
.match(/.*?\(([^)]*)\)/)[1].split(",").map(arg => {
return removeComments(arg).trim()
}).filter(arg => {
return arg
}),
body = ctx.request.body,
query = ctx.request.query
const reflectDatas = [{
name: 'queries',
data: query
}, {
name: 'bodyParams',
data: body
}]
// 将请求中的数据装载到对应的参数位置中去
reflectDatas.forEach(reflectData => {
const methodParams = Reflect
.getMetadata(reflectData.name, method)
for (let i = 0;methodParams && i < methodParams.length;i++) {
const methodParam = methodParams[i]
// 处理参数数据
instanceArgs[methodParam.index] = reflectData.data[methodParam.name]
}
})
const indexDatas = [{
name: 'ctxIndex',
instance: ctx
}, ...其他的特殊参数]
indexDatas.forEach(indexData => {
// 向参数列表中指定位置赋值变量
})
// 获取控制层的返回数据
let returnBody
if (instanceArgs.length === 0) {
returnBody = await instance[methodName](ctx)
} else {
returnBody = await instance[methodName](...instanceArgs)
}
if (returnBody !== undefined) {
ctx.body = returnBody
}
})
})
// 向koa的中间层注册路由
app.use(router.routes())
app.use(router.allowedMethods())
}
}
oldListen.call(app, ...args)
}
}