[Typescript]用装饰器封装Express服务(7)-前置组件实现

164 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情

设计

前置组件、或者中间件定义为,请求正式进入controller之前,需要执行的中间件。比如,jwt校验,跨域处理等。

设计一个前置组件装饰器,接受两个参数:排序和名称,扫描时,将其实例化并保持到相应的缓存中。


@PreComponent

在完成ws服务的部署之后,进行前置组件的依次调用,加载如express中

实现

1.装饰器@PreComponent的实现


export const PreComponent = (index: number = 100, componentName?: string): ClassDecorator => {
    return (constructor: any) => {
        let id = getTargetId(constructor)
        if (!componentName) {
            componentName = constructor.name + '_' + id
        }
        constructor.prototype.index = index
        constructor.prototype.name = constructor.name
        Reflect.defineMetadata('preComponent', constructor.name, constructor);
        log(`[PreComponent]- add PreComponent: ${constructor.name}`)
        // 可直接new一个不需要代理
        const instance = proxify(new constructor());
        application.componentManager.addPreComponents(componentName, instance)
        application.componentManager.addBean(componentName, constructor, instance)
    };
}

将实例保存至两个集合中,方便后续获取依赖等操作

2.相关保存实例的方法


// class

export class ComponentManager {
    preComponents?: Map<string, ComponentInfo> = new Map(); // 添加Controller之前需要添加的组件集合
    componentsOnName?: Map<string, ComponentInfo> = new Map();    // 普通组件集合
    componentsOnKey?: Map<any, ComponentInfo> = new Map();    // 普通组件集合
    injectInfos: Map<string, InjectInfo[]> = new Map();
    ...
    }
    
    /////
    /**
    * 
    * @param componentName 组件名称
    * @param originClass 组件class
    * @param instance 组件实例
    */
    public async addBean(componentName: string, originClass: any, instance: any) {
        let _componentName;
        componentName = ((_componentName = componentName) !== null && _componentName !== void 0 ? _componentName : originClass.name);
        let component = {
            className: originClass.name,
            componentName,
            status: 'wired',
            value: originClass,
            instance: instance,
        };
        //autoWiringComponents[originClass] = autoWiringComponents[componentName]
        this.addComponents(componentName, component)
        log(`[Component]-load component:${componentName} ${originClass.name}`)
    }
    
    //////
        public async addPreComponents(name: string, con: any) {
        this.preComponents.set(name, con)
    }


3.加载组件

    public loadPreComponents(app): void {
        log(`========================= load preComponent========================`)
        let array = []
        for (let [key, value] of this.preComponents.entries()) {
            // log(key)
            array.push(value)
        }
        // 从小到大的排序 
        array.sort((Acomponent, Bcomponent) => {
            return Acomponent.index - Bcomponent.index;
        });
        //
        array.forEach(component => {
            log(`[preComponent]- load preComponent: ${component.name}`)
            if (component && component.enable) {
                component.enable(app)
            }
        })
    }


默认是调用组件实例的enable方法,将express app当做参数插入 例如,配置app服务的请求配置


@PreComponent(0)
class WebConfig{

    enable(app){
        log(`[WebConfig]- enable WebConfig`)
        app.use(cookieparser());
        // 中间件
        app.use(express.json({ limit: '5mb' }));
        app.use(express.urlencoded({ extended: true }));
        // let s_path = path.join(process.cwd(), 'ui/dist')
        // app.use(express.static(s_path));

        app.options('*', function (req, res, next) {
            res.setHeader('Access-Control-Allow-Origin', '*');
            res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
            res.setHeader('Access-Control-Allow-Headers', 'Authorization,X-Requested-With,content-type');
            res.setHeader('Access-Control-Allow-Credentials', 'true');
            next();
        });
    }
}