From 878a5368afa05d2d3b0e4b831fcf25a144e7ae1a Mon Sep 17 00:00:00 2001 From: MiaoWoo Date: Mon, 6 Jul 2020 16:10:33 +0800 Subject: [PATCH] feat: add plugin web stage Signed-off-by: MiaoWoo --- packages/web/src/decorators/index.ts | 18 ++++--- packages/web/src/decorators/utils.ts | 10 ++-- packages/web/src/interfaces/metadata.ts | 2 +- packages/web/src/server.ts | 64 +++++++++++++++++-------- 4 files changed, 64 insertions(+), 30 deletions(-) diff --git a/packages/web/src/decorators/index.ts b/packages/web/src/decorators/index.ts index 8d251d6f..c3152b94 100644 --- a/packages/web/src/decorators/index.ts +++ b/packages/web/src/decorators/index.ts @@ -1,20 +1,29 @@ -import { decorate, injectable } from "@ccms/container" +import { plugin } from '@ccms/api' +import { decorate, injectable, getContainer } from "@ccms/container" import { METADATA_KEY, PARAM_TYPE } from '../constants' import { interfaces } from "../interfaces" -import { addControllerMetadata, addControllerAction, addActionParam } from "./utils" +import { addControllerMetadata, addControllerAction, addActionParam, getControllerMetadata } from "./utils" + +export const Controllers = (...controllers: any[]) => { + return (target: any, propertyKey: string) => { + for (const controller of controllers) { + addControllerMetadata(getControllerMetadata(controller), target) + } + } +} export const Controller = (metadata?: string | interfaces.ControllerMetadata) => { return (target: any) => { if (!metadata) { metadata = target.name.toLowerCase().replace('controller', '') } if (typeof metadata === "string") { metadata = { path: metadata } } + metadata.target = target metadata.name = metadata.name || target.name metadata.path = metadata.path ?? `/${metadata}` metadata.path = metadata.path.startsWith('/') ? metadata.path : `/${metadata.path}` decorate(injectable(), target) Reflect.defineMetadata(METADATA_KEY.Controller, metadata, target) addControllerMetadata(metadata) - return } } @@ -29,7 +38,6 @@ function action(method: interfaces.Method) { metadata.executor = propertyKey Reflect.defineMetadata(METADATA_KEY.Action, metadata, target[propertyKey]) addControllerAction(target, propertyKey) - return } } } @@ -50,7 +58,6 @@ function param(type: PARAM_TYPE) { metadata.index = index metadata.paramtype = Reflect.getMetadata("design:paramtypes", target, propertyKey)[index] addActionParam(target, propertyKey, metadata) - return } } } @@ -72,7 +79,6 @@ function Middleware() { metadata.executor = propertyKey Reflect.defineMetadata(METADATA_KEY.Action, metadata, target[propertyKey]) addControllerAction(target, propertyKey) - return } } } diff --git a/packages/web/src/decorators/utils.ts b/packages/web/src/decorators/utils.ts index 3f858163..6f3cde8e 100644 --- a/packages/web/src/decorators/utils.ts +++ b/packages/web/src/decorators/utils.ts @@ -1,11 +1,13 @@ import { interfaces } from '../interfaces' import { METADATA_KEY } from '../constants' -export function getControllerMetadatas(): interfaces.ControllerMetadata[] { - return Reflect.getMetadata(METADATA_KEY.Controller, Reflect) || [] +export function getControllerMetadatas(target: any = Reflect): Map { + return Reflect.getMetadata(METADATA_KEY.Controller, target) || new Map() } -export function addControllerMetadata(metadata: interfaces.ControllerMetadata) { - Reflect.defineMetadata(METADATA_KEY.Controller, [metadata, ...getControllerMetadatas()], Reflect) +export function addControllerMetadata(metadata: interfaces.ControllerMetadata, target: any = Reflect) { + let metadatas = getControllerMetadatas(target) + metadatas.set(metadata.name, metadata) + Reflect.defineMetadata(METADATA_KEY.Controller, metadatas, target) } export function getControllerActions(target: any): string[] { return Reflect.getMetadata(METADATA_KEY.Action, target.constructor) || [] diff --git a/packages/web/src/interfaces/metadata.ts b/packages/web/src/interfaces/metadata.ts index 90bd95d2..b9a7eee8 100644 --- a/packages/web/src/interfaces/metadata.ts +++ b/packages/web/src/interfaces/metadata.ts @@ -19,7 +19,7 @@ export namespace interfaces { /** * 对象 */ - target?: string + target?: any } export interface ControllerMetadata extends WebMetadata { diff --git a/packages/web/src/server.ts b/packages/web/src/server.ts index 1138c84e..e0639e72 100644 --- a/packages/web/src/server.ts +++ b/packages/web/src/server.ts @@ -1,11 +1,11 @@ import * as querystring from 'querystring' -import { web } from '@ccms/api' +import { web, plugin } from '@ccms/api' import { provideSingleton, JSClass, postConstruct, Container, ContainerInstance, inject } from '@ccms/container' import { WebProxyBeanName, FilterProxyBeanName, METADATA_KEY, PARAM_TYPE } from './constants' import { Context, InterceptorAdapter, RequestHandler, interfaces } from './interfaces' -import { getControllerActions, getActionMetadata, getControllerMetadata, getActionParams } from './decorators' +import { getControllerActions, getActionMetadata, getControllerMetadata, getActionParams, getControllerMetadatas } from './decorators' const HttpServletRequestWrapper = Java.type('javax.servlet.http.HttpServletRequestWrapper') const HttpServletResponseWrapper = Java.type('javax.servlet.http.HttpServletResponseWrapper') @@ -25,6 +25,7 @@ export class Server { private StreamUtils = org.springframework.util.StreamUtils private ResponseEntity = org.springframework.http.ResponseEntity + private pluginControllers: Map private interceptors: Map private methodMappings: Map> @@ -36,6 +37,8 @@ export class Server { this.interceptors = new Map() this.methodMappings = new Map() this.start() + process.on('plugin.after.enable', (plugin: plugin.Plugin) => this.registryPlugin(plugin)) + process.on('plugin.after.disable', (plugin: plugin.Plugin) => this.unregistryPlugin(plugin)) } start() { @@ -52,23 +55,34 @@ export class Server { } } + registryPlugin(plugin: plugin.Plugin) { + let controllers = getControllerMetadatas(plugin).values() + for (const controller of controllers) { + console.debug(`Plugin ${plugin.description.name} Registry Controller ${controller.name}.`) + this.registryController(controller.target) + } + } + + unregistryPlugin(plugin: plugin.Plugin) { + let controllers = getControllerMetadatas(plugin).values() + for (const controller of controllers) { + console.debug(`Plugin ${plugin.description.name} Unregistry Controller ${controller.name}.`) + this.unregistryController(controller.target) + } + } + registryController(target: any) { if (!target) { throw new Error('Controller can\'t be undefiend!') } let controllerMetadata = getControllerMetadata(target) if (!controllerMetadata) { throw new Error(`Controller ${target.name} must have @Controller decorator!`) } - try { - this.container.rebind(METADATA_KEY.Controller).to(target).inSingletonScope().whenTargetNamed(target.name) - } catch{ - this.container.bind(METADATA_KEY.Controller).to(target).inSingletonScope().whenTargetNamed(target.name) - } - target = this.container.getNamed(METADATA_KEY.Controller, target.name) + target = this.bindController(target) let actions = getControllerActions(target) for (const action of actions) { let actionMetadata = getActionMetadata(target, action) let path = `${controllerMetadata.path || ''}${actionMetadata.path || ''}` if (!path) throw new Error(`Controller ${controllerMetadata.name} Action ${actionMetadata.name} path is empty!`) if (!this.methodMappings.has(path)) { this.methodMappings.set(path, new Map()) } - console.debug(`Controller ${controllerMetadata.name} Registry ${path} to ${actionMetadata.executor || ''} Action function.`) + console.debug(`Controller ${controllerMetadata.name} Registry ${path} Action to ${actionMetadata.executor || ''} function.`) this.methodMappings.get(path).set(actionMetadata.method || 'ALL', (ctx: Context) => { let args = [] let params = getActionParams(target, action) @@ -97,6 +111,15 @@ export class Server { } } + private bindController(target: any) { + try { + this.container.rebind(METADATA_KEY.Controller).to(target).inSingletonScope().whenTargetNamed(target.name) + } catch{ + this.container.bind(METADATA_KEY.Controller).to(target).inSingletonScope().whenTargetNamed(target.name) + } + return this.container.getNamed(METADATA_KEY.Controller, target.name) + } + unregistryController(target: any) { if (!target) { throw new Error('Controller can\'t be undefiend!') } let controllerMetadata = getControllerMetadata(target) @@ -112,6 +135,7 @@ export class Server { let path = `${controllerMetadata.path || ''}${actionMetadata.path || ''}` if (!this.methodMappings.has(path)) { continue } this.methodMappings.get(path).delete(actionMetadata.method) + console.debug(`Controller ${controllerMetadata.name} Unregistry ${path} Action.`) } } @@ -130,7 +154,9 @@ export class Server { this.interceptors.set(interceptor.name, interceptor) } - unregistryInterceptor(interceptor: InterceptorAdapter) { + unregistryInterceptor(interceptor: string | InterceptorAdapter) { + if (typeof interceptor === "string") { interceptor = { name: interceptor } } + console.debug(`Unregistry ${interceptor.name} Interceptor.`) this.interceptors.delete(interceptor.name) } @@ -170,12 +196,12 @@ export class Server { // return wrapper // } - private notFound(method: string, path: string) { + private notFound(ctx: Context) { return { status: 404, msg: "handlerMapping Not Found!", - method, - path, + method: ctx.request.getMethod(), + path: ctx.request.getRequestURI(), timestamp: Date.now() } } @@ -184,12 +210,7 @@ export class Server { try { this.beanFactory.destroySingleton(WebProxyBeanName) } catch (ex) { } var WebServerProxyNashorn = Java.extend(this.WebServerProxy, { process: (req: javax.servlet.http.HttpServletRequest, resp: javax.servlet.http.HttpServletResponse) => { - let path = req.getRequestURI() - if (!this.methodMappings.has(path)) return this.notFound(req.getMethod(), path) - let mappings = this.methodMappings.get(req.getRequestURI()) - let handler = mappings.get(req.getMethod()) || mappings.get("ALL") - if (!handler) return this.notFound(req.getMethod(), path) - let ctx: Context = { request: req, response: resp, params: {}, body: {}, handler } + let ctx: Context = { request: req, response: resp, params: {}, body: {} } ctx.url = req.getRequestURI() // @ts-ignore ctx.headers = { __noSuchProperty__: (name: string) => req.getHeader(name) } @@ -250,6 +271,10 @@ export class Server { } } } + let path = ctx.request.getRequestURI() + if (!this.methodMappings.has(path)) return this.notFound(ctx) + let mappings = this.methodMappings.get(ctx.request.getRequestURI()) + ctx.handler = mappings.get(ctx.request.getMethod()) || mappings.get("ALL") ctx.result = this.execRequestHandle(ctx) for (const [_, interceptor] of this.interceptors) { if (interceptor.postHandle) { @@ -283,6 +308,7 @@ Handle Time : ${Date.now() - startTime}ms } private execRequestHandle(ctx: Context) { + if (!ctx.handler) return this.notFound(ctx) try { return ctx.handler(ctx) } catch (error) {