// 自动扫描 controllers 目录并注册路由 // 兼容传统 routes 方式和自动注册 controller 方式 import fs from "fs" import path from "path" /** * 自动扫描 controllers 目录,注册所有导出的路由 * 自动检测 routes 目录下已手动注册的 controller,避免重复注册 * @param {Koa} app - Koa 实例 * @param {string} controllersDir - controllers 目录路径 * @param {string} prefix - 路由前缀 * @param {Set} [manualControllers] - 可选,手动传入已注册 controller 文件名集合,优先于自动扫描 */ export function autoRegisterControllers(app, controllersDir = path.resolve(__dirname, "../controllers")) { let allRouter = [] async function scan(dir, routePrefix = "") { for (const file of fs.readdirSync(dir)) { const fullPath = path.join(dir, file) const stat = fs.statSync(fullPath) if (stat.isDirectory()) { await scan(fullPath, routePrefix + "/" + file) } else if (file.endsWith("Controller.js")) { let controller try { controller = require(fullPath) } catch (e) { controller = (await import(fullPath)).default } const routes = controller.createRoutes || controller.default?.createRoutes || controller.default || controller if (typeof routes === "function") { allRouter.push(routes()) } } } } ;(async () => { await scan(controllersDir) // TODO: 存在问题:每个Controller都是有顺序的,如果其中一个Controller没有next方法,可能会导致后续的Controller无法执行 // allRouter.forEach(router => { // app.use(router.middleware()) // }) // 聚合中间件:只分发到匹配的router app.use(async (ctx, next) => { let matched = false for (const router of allRouter) { // router._matchRoute 只在 router.js 内部,需暴露或用 middleware 包一层 if (typeof router._matchRoute === "function") { const route = router._matchRoute(ctx.method.toLowerCase(), ctx.path) if (route) { matched = true await router.middleware()(ctx, next) break // 命中一个即停止 } } else { // fallback: 直接尝试middleware,若未命中会自动next const before = ctx.status await router.middleware()(ctx, next) if (ctx.status !== before) { matched = true break } } } if (!matched) { await next() } }) })() }