You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Microsoft/TypeScript-Node-Starter模板,如何在控制器中获取Express app实例?

解决Express + TypeScript中的循环依赖与路由获取问题

这确实是典型的循环依赖问题——你的app.ts里导入了apiController,而apiController.ts又反过来导入app,Node.js加载模块时,这种互相引用会导致其中一个模块在未完全初始化时就被引用,所以你拿到的app是undefined。下面给你几个实用的解决方案:

方案1:重构代码,避免直接依赖app对象

既然你要获取路由列表,其实不需要硬拉整个app对象,换个思路就能避开循环:

方法1.1:在app初始化完成后传递路由栈

修改app.ts,等所有路由挂载完成后,把路由栈传递给控制器:

// app.ts
import * as express from 'express';
import * as apiController from './controllers/api';

const app = express();

// 先挂载所有路由
app.get('/api', apiController.getApi);

// 把初始化好的路由栈传给控制器
apiController.setRouterStack(app._router.stack);

module.exports = app;

然后更新api.ts,用接收的路由栈来处理:

// controllers/api.ts
import { Request, Response } from 'express';

let routerStack: any[] = [];

// 接收外部传递的路由栈
export function setRouterStack(stack: any[]) {
  routerStack = stack;
}

export let getApi = (req: Request, res: Response) => {
  // 过滤并整理出可用的API端点
  const apiEndpoints = routerStack
    .filter(layer => layer.route)
    .map(layer => ({
      path: layer.route.path,
      method: Object.keys(layer.route.methods)[0].toUpperCase()
    }));
  
  res.json({ apiEndpoints });
}

方法1.2:抽离路由注册逻辑,延迟获取app

把路由注册放到独立文件,再通过一个方法延迟获取初始化完成的app:

  1. 创建routes/index.ts统一管理路由:
// routes/index.ts
import { Express } from 'express';
import * as apiController from '../controllers/api';

export function registerRoutes(app: Express) {
  app.get('/api', apiController.getApi);
  // 其他路由也可以在这里统一注册
}
  1. 修改app.ts,暴露一个获取初始化后app的方法:
// app.ts
import * as express from 'express';
import { registerRoutes } from './routes';

const app = express();

// 先完成路由注册
registerRoutes(app);

// 提供一个方法,让外部能拿到完全初始化的app
export const getApp = () => app;

module.exports = app;
  1. 更新api.ts,在请求处理时再获取app(此时app已经完全初始化):
// controllers/api.ts
import { Request, Response } from 'express';
import { getApp } from '../app';

export let getApi = (req: Request, res: Response) => {
  const app = getApp();
  const apiEndpoints = app._router.stack
    .filter(layer => layer.route)
    .map(layer => ({
      path: layer.route.path,
      method: Object.keys(layer.route.methods)[0].toUpperCase()
    }));
  
  res.json({ apiEndpoints });
}

方案2:统一模块规范,延迟访问app对象

你当前混合使用了module.exports和ES模块的import/export,这会加重模块加载的混乱。建议统一用ES模块:

修改app.ts:

// app.ts
import * as express from 'express';
import { registerRoutes } from './routes';

// 用ES模块导出app
export const app = express();

// 完成路由注册
registerRoutes(app);

// 兼容CommonJS(如果需要)
module.exports = app;

然后api.ts里直接导入,但等到请求处理时再访问app(此时模块已经全部加载完成):

// controllers/api.ts
import { Request, Response } from 'express';
import { app } from '../app';

export let getApi = (req: Request, res: Response) => {
  // 此时app已经完全初始化,不会是undefined
  const apiEndpoints = app._router.stack
    .filter(layer => layer.route)
    .map(layer => ({
      path: layer.route.path,
      method: Object.keys(layer.route.methods)[0].toUpperCase()
    }));
  
  res.json({ apiEndpoints });
}

更可靠的替代方案:自己维护路由列表

直接操作app._router.stack属于依赖Express的内部私有实现(下划线开头的属性不对外公开,版本更新可能会变),更稳妥的方式是在注册路由时自己维护一个列表:

  1. 更新routes/index.ts:
// routes/index.ts
import { Express } from 'express';
import * as apiController from '../controllers/api';

// 手动维护所有注册的路由
export const registeredRoutes = [
  { path: '/api', method: 'GET', handler: apiController.getApi }
  // 新增路由时直接在这里添加
];

export function registerRoutes(app: Express) {
  registeredRoutes.forEach(route => {
    app[route.method.toLowerCase()](route.path, route.handler);
  });
}
  1. 修改api.ts直接用这个列表:
// controllers/api.ts
import { Request, Response } from 'express';
import { registeredRoutes } from '../routes';

export let getApi = (req: Request, res: Response) => {
  // 直接返回维护好的路由信息,完全避开循环依赖
  res.json({ 
    apiEndpoints: registeredRoutes.map(r => ({ 
      path: r.path, 
      method: r.method 
    })) 
  });
}

这个方式不仅彻底解决了循环依赖,还让路由管理更清晰,也不用担心Express版本更新导致的兼容性问题。

内容的提问来源于stack exchange,提问作者user66875

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:45:36