大型项目路由拆分方案
路由拆分背景
随着项目规模扩大,单个路由文件会变得臃肿难维护。一个典型的Koa2项目初始阶段可能把所有路由都写在app.js里,当路由数量超过50个时,文件可能达到上千行代码。这种情况下,查找特定路由变得困难,团队协作时容易产生冲突,测试用例也难以定位。
基础路由拆分方案
最直接的方式是按业务模块拆分路由文件。假设有个电商项目,可以创建以下目录结构:
routes/
├── user.js
├── product.js
├── order.js
└── cart.js
每个文件导出自己的路由实例:
// routes/user.js
const router = require('koa-router')();
router.get('/users', async (ctx) => {
ctx.body = await UserService.list();
});
router.post('/users', async (ctx) => {
ctx.body = await UserService.create(ctx.request.body);
});
module.exports = router;
然后在主文件中统一加载:
// app.js
const userRouter = require('./routes/user');
const productRouter = require('./routes/product');
app.use(userRouter.routes());
app.use(productRouter.routes());
自动化路由加载
当路由文件增多时,手动require每个文件变得繁琐。可以使用fs模块自动加载路由:
const fs = require('fs');
const path = require('path');
const routesPath = path.join(__dirname, 'routes');
fs.readdirSync(routesPath).forEach(file => {
const router = require(path.join(routesPath, file));
app.use(router.routes());
});
多级路由结构
对于更复杂的项目,可能需要多级路由。例如管理后台和用户端API分开:
routes/
├── admin/
│ ├── user.js
│ └── system.js
└── api/
├── v1/
│ ├── user.js
│ └── product.js
└── v2/
├── user.js
└── order.js
实现方式是在子路由中使用prefix:
// routes/api/v1/user.js
const router = require('koa-router')({
prefix: '/api/v1'
});
router.get('/users', ...);
路由中间件隔离
不同路由可能需要不同的中间件组合。可以在路由级别配置中间件:
// routes/admin/user.js
const auth = require('../../middlewares/admin-auth');
const router = require('koa-router')();
router.use(auth);
router.get('/users', ...);
动态路由注册
某些场景需要根据配置动态注册路由:
// routes/dynamic.js
module.exports = function(config) {
const router = require('koa-router')();
config.routes.forEach(route => {
router[route.method](route.path, ...route.middlewares);
});
return router;
}
路由元信息管理
为路由添加元数据便于统一管理:
// decorators/route.js
function GET(path) {
return function(target, name, descriptor) {
const handler = descriptor.value;
handler.__route = {
method: 'get',
path,
handler
};
return descriptor;
}
}
// routes/user.js
class UserController {
@GET('/users')
async listUsers(ctx) {
// ...
}
}
性能优化考虑
路由数量过多时会影响匹配性能。可以:
- 将高频路由前置
- 使用路由缓存
- 避免复杂正则路径
// 高频路由单独处理
const frequentRouter = require('koa-router')();
frequentRouter.get('/api/products/hot', ...);
app.use(frequentRouter.routes());
// 其他路由
app.use(mainRouter.routes());
测试友好设计
为方便测试,路由文件应该:
- 导出原始router实例
- 不自动注册中间件
- 支持依赖注入
// routes/user.js
module.exports = function(userService) {
const router = require('koa-router')();
router.get('/users', async ctx => {
ctx.body = await userService.list();
});
return router;
}
版本控制方案
API版本演进时,推荐以下几种方案:
- URL路径版本控制
// routes/v1/user.js
router.prefix('/v1/users');
- 请求头版本控制
router.get('/users', ctx => {
const version = ctx.headers['x-api-version'] || 'v1';
// 根据version返回不同数据
});
- 查询参数版本控制
router.get('/users', ctx => {
const version = ctx.query.version || 'v1';
});
路由文档生成
结合JSDoc自动生成路由文档:
/**
* @api {get} /users 获取用户列表
* @apiVersion 1.0.0
* @apiName GetUsers
*/
router.get('/users', ...);
使用工具解析注释生成API文档。
错误处理统一
每个路由文件可以定义自己的错误处理:
// routes/user.js
router.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.body = {
error: 'User module error',
details: err.message
};
}
});
路由权限控制
实现细粒度的权限控制:
// routes/admin.js
const permissions = {
'/users': ['admin'],
'/settings': ['superadmin']
};
router.use(async (ctx, next) => {
const userRole = ctx.state.user.role;
const requiredRoles = permissions[ctx.path];
if (!requiredRoles.includes(userRole)) {
ctx.throw(403);
}
await next();
});
路由性能监控
添加路由级性能追踪:
router.use(async (ctx, next) => {
const start = Date.now();
await next();
const duration = Date.now() - start;
metrics.track(ctx.path, duration);
});
路由缓存策略
根据路由特性设置缓存:
// routes/product.js
const cache = require('../middlewares/cache');
router.get('/products/:id',
cache({ ttl: 3600 }),
async ctx => {
// ...
}
);
路由流量控制
限制高频路由的访问:
const rateLimit = require('koa-ratelimit');
router.get('/api/search',
rateLimit({
duration: 60000,
max: 30
}),
async ctx => {
// ...
}
);
路由参数校验
使用Joi等库校验参数:
const Joi = require('joi');
router.post('/users',
validate({
body: Joi.object({
name: Joi.string().required(),
email: Joi.string().email().required()
})
}),
async ctx => {
// ...
}
);
微服务路由网关
在微服务架构中作为网关路由:
const { proxy } = require('koa-http-proxy');
router.all('/user-service/(.*)',
proxy({
target: 'http://user-service:3000',
rewrite: path => path.replace('/user-service', '')
})
);
路由配置中心
从配置中心动态加载路由:
const configClient = require('config-client');
router.use(async (ctx, next) => {
const routes = await configClient.get('routes');
const routeConfig = routes.find(r => r.path === ctx.path);
if (routeConfig?.maintenance) {
ctx.status = 503;
return;
}
await next();
});
路由AB测试
支持路由级别的AB测试:
router.get('/new-feature',
abTest({
variants: [
{ weight: 50, handler: variantAHandler },
{ weight: 50, handler: variantBHandler }
]
})
);
路由数据mock
开发环境mock路由数据:
const isDev = process.env.NODE_ENV === 'development';
router.get('/products',
isDev ? mockHandler : realHandler
);
function mockHandler(ctx) {
ctx.body = [{ id: 1, name: 'Mock Product' }];
}
路由请求转换
统一处理请求数据:
router.use(async (ctx, next) => {
// 转换驼峰命名到下划线
if (ctx.request.body) {
ctx.request.body = convertKeys(ctx.request.body);
}
await next();
});
路由响应格式化
统一响应格式:
router.use(async (ctx, next) => {
await next();
if (ctx.body && !ctx.body.error) {
ctx.body = {
code: 200,
data: ctx.body,
timestamp: Date.now()
};
}
});
路由健康检查
添加健康检查端点:
router.get('/health', (ctx) => {
ctx.body = {
status: 'UP',
services: {
db: checkDb(),
cache: checkCache()
}
};
});
路由过时标记
标记即将废弃的路由:
router.get('/old-api',
deprecated({
message: 'Use /new-api instead',
sunset: '2023-12-31'
}),
oldApiHandler
);
路由实验特性
隔离实验性功能:
router.get('/experimental/feature-x',
experimental({
flag: 'FEATURE_X_ENABLED'
}),
experimentalHandler
);
路由流量染色
为特定路由标记流量:
router.use('/admin', (ctx, next) => {
ctx.state.tags = ctx.state.tags || [];
ctx.state.tags.push('admin');
return next();
});
路由数据脱敏
敏感数据自动脱敏:
router.use('/users', (ctx, next) => {
await next();
if (ctx.body?.email) {
ctx.body.email = maskEmail(ctx.body.email);
}
});
路由请求录制
录制生产环境请求:
router.use((ctx, next) => {
if (shouldRecord(ctx.path)) {
requestRecorder.record(ctx);
}
return next();
});
路由智能降级
异常时自动降级:
router.get('/recommendations',
circuitBreaker({
fallback: getCachedRecommendations
}),
getLiveRecommendations
);
路由蓝绿部署
支持蓝绿部署切换:
router.get('/products',
blueGreen({
blue: blueHandler,
green: greenHandler,
selector: ctx => ctx.headers['x-deployment-group']
})
);
路由地域路由
根据地域路由请求:
router.get('/content',
geoRouter({
'US': usContentHandler,
'EU': euContentHandler,
'default': globalContentHandler
})
);
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:路由性能监控与分析
下一篇:HTTP 请求方法全面解析