使用express-promise-router的分层路由Express应用如何列出所有注册路由?
express-promise-router分层路由全量获取方案
实现原理
express-promise-router本质是对express原生Router的薄封装,实例内部仍保留原生Router的stack属性存储路由栈,第三方工具无法识别分层路由的核心原因是没有做递归遍历处理,只要按以下逻辑逐层解析即可拿到所有路由:
- 每个express实例(包括app、各级子路由)的路由都存储在自身的
stack数组中,数组每一项为layer对象 - 若layer存在
route属性,说明为最终接口路由,可直接获取请求方法和路径后缀 - 若layer的
name为router,说明为子路由挂载层,其handle属性(或包装后的handle.router属性)对应子路由实例,需拼接当前层级路径前缀后递归遍历子路由的stack
具体实现代码
/** * 提取所有路由 * @param {Object} app express实例 / 顶层promise router实例 * @param {String} basePath 根路径前缀,默认空 * @returns {Array} 路由列表,包含path(完整路径)、methods(请求方法数组) */ function listAllRoutes(app, basePath = '') { const routes = []; function traverseStack(stack, currentPrefix) { stack.forEach(layer => { // 处理最终接口路由 if (layer.route) { const fullPath = currentPrefix + layer.route.path; const methods = Object.keys(layer.route.methods).map(method => method.toUpperCase()); routes.push({ path: fullPath, methods: methods }); return; } // 处理子路由层,兼容原生router和express-promise-router的包装结构 const childRouter = layer.handle?.stack ? layer.handle : layer.handle?.router; if (layer.name === 'router' && childRouter?.stack) { // 提取子路由挂载前缀,兼容新旧版本express的路径取值 let subPrefix = layer.path || ''; if (!subPrefix) { // 旧版本express无layer.path属性,从正则反向提取路径 subPrefix = layer.regexp.toString() .replace(/^\/\^/, '') .replace(/\\\/\?\(\?=\/\|\$\)/, '') .replace(/\\/g, ''); } traverseStack(childRouter.stack, currentPrefix + subPrefix); } }); } // 遍历根路由栈 const rootStack = app._router?.stack || app.stack; if (rootStack) { traverseStack(rootStack, basePath); } return routes; } // 使用示例,服务启动完成后调用即可 const allRoutes = listAllRoutes(app); // 打印所有路由 console.log('===== 项目所有路由列表 ====='); allRoutes.forEach(item => { console.log(`${item.methods.join(',')} ${item.path}`); });
适配注意事项
- 如果项目中存在正则格式的路由路径,可自行调整子前缀提取的正则逻辑适配业务规则
- 支持任意层级的嵌套子路由,无需针对嵌套层数做额外配置
- 如不需要从app实例遍历,也可以直接将顶层的promise router实例作为参数传入函数,可拿到相同结果
内容的提问来源于stack exchange,提问作者Subhi Samara
相关产品推荐
相关产品推荐

