如何在Node.js Express中实现嵌套递归式API路由功能
Express实现多层嵌套递归路由的方案
核心思路
这类嵌套API路径本质是资源类型/资源标识的重复组合,比如/users/54282/friends/0就是两组资源对:[users, 54282]、[friends, 0],我们只需要把路径拆分后按顺序递归查询资源即可。
方案1:正则路由全匹配+自行解析路径(最简便)
直接用Express的正则路由规则捕获所有嵌套段,手动拆分路径后逐层处理资源,示例代码如下:
const express = require('express'); const app = express(); // 模拟数据,实际替换为自己的数据库查询逻辑 const mockData = { users: { 54282: { id: 54282, name: '张三', friends: [ { id: 97420, name: '李四', friends: [ { id: 31230, name: '王五', friends: [] } ] } ] } } } // 逐层处理资源链 async function resolveResourceChain(pairs) { let current = null; for (const [type, id] of pairs) { if (!current) { // 第一层资源直接从根查询 current = mockData[type]?.[id]; } else { // 嵌套资源从上级资源的对应属性查询 const resourceList = current[type]; // 支持数字索引或者ID匹配 current = /^\d+$/.test(id) ? resourceList?.[+id] : resourceList?.find(item => item.id == id); } if (!current) throw new Error(`资源 ${type}/${id} 不存在`); } return current; } // 匹配所有/api开头的嵌套资源路径 app.get(/^\/api((?:\/\w+\/[^/]+)+)/, async (req, res) => { try { // 拆分路径得到["users", "54282", "friends", "0", "friends", "0"]格式的数组 const segments = req.params[0].split('/').filter(Boolean); // 两两分组得到资源对 const resourcePairs = []; for (let i = 0; i < segments.length; i += 2) { resourcePairs.push([segments[i], segments[i+1]]); } // 限制最大嵌套层数,避免恶意请求 if (resourcePairs.length > 6) return res.status(400).json({error: '嵌套层数过多,最多支持6层'}); const result = await resolveResourceChain(resourcePairs); res.json(result); } catch (e) { res.status(404).json({error: e.message}); } }); app.listen(3000, () => console.log('服务运行在 http://localhost:3000'));
启动服务后访问GET http://localhost:3000/api/users/54282/friends/0/friends/0就可以直接得到最终的王五用户数据。
方案2:递归挂载子资源路由(更灵活)
如果需要对不同资源做单独的权限校验、逻辑处理,可以给每个资源注册可嵌套的路由:
const express = require('express'); const app = express(); const apiRouter = express.Router(); // 注册资源的处理逻辑和子资源关联 const resourceConfig = { users: { handler: (id) => mockData.users[id], children: ['friends', 'repos'] }, friends: { handler: (parent, id) => /^\d+$/.test(id) ? parent.friends[+id] : parent.friends.find(f => f.id == id), children: ['friends'] }, repos: { handler: (parent, id) => parent.repos.find(r => r.id == id), children: ['collaborators'] }, collaborators: { handler: (parent, id) => /^\d+$/.test(id) ? parent.collaborators[+id] : parent.collaborators.find(c => c.id == id), children: ['repos'] } } // 递归挂载路由 function mountResourceRouter(parentRouter, resourceType, parentResource = null) { parentRouter.get(`/${resourceType}/:id/*?`, async (req, res, next) => { try { const currentResource = resourceConfig[resourceType].handler(parentResource, req.params.id); if (!currentResource) return res.status(404).json({error: `${resourceType}不存在`}); // 检查是否还有后续路径,如果有就挂载子资源路由继续处理 const remainingPath = req.params[0] || ''; if (remainingPath) { const childType = remainingPath.split('/')[0]; if (resourceConfig[resourceType].children.includes(childType)) { const childRouter = express.Router({mergeParams: true}); mountResourceRouter(childRouter, childType, currentResource); return childRouter(req, res, next); } return res.status(404).json({error: '不支持的子资源'}); } // 没有后续路径直接返回当前资源 res.json(currentResource); } catch (e) { next(e); } }); } // 初始化根资源路由 mountResourceRouter(apiRouter, 'users'); app.use('/api', apiRouter);
注意事项
- 每一层资源查询都需要加上权限校验,避免未授权用户越权访问嵌套资源
- 必须限制最大嵌套层数,防止恶意构造超长嵌套路径导致服务性能下降
- 高频访问的嵌套资源可以增加缓存,降低数据库压力
内容的提问来源于stack exchange,提问作者user16238654
相关产品推荐
相关产品推荐

