Appearance
Koa路由管理
Koa本身不提供路由功能,但可以通过第三方中间件如@koa/router来实现路由管理。路由是指如何处理不同的URL路径和HTTP方法。
安装路由中间件
bash
npm install @koa/router
基础路由
基本路由定义
javascript
const Koa = require('koa');
const Router = require('@koa/router');
const app = new Koa();
const router = new Router();
// GET路由
router.get('/', (ctx, next) => {
ctx.body = 'Home page';
});
// POST路由
router.post('/users', (ctx, next) => {
ctx.body = 'Create user';
});
// PUT路由
router.put('/users/:id', (ctx, next) => {
ctx.body = `Update user ${ctx.params.id}`;
});
// DELETE路由
router.delete('/users/:id', (ctx, next) => {
ctx.body = `Delete user ${ctx.params.id}`;
});
// 应用路由中间件
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000);
多种HTTP方法路由
javascript
// 处理多种HTTP方法
router
.get('/users', (ctx) => {
ctx.body = 'Get all users';
})
.post('/users', (ctx) => {
ctx.body = 'Create user';
})
.put('/users/:id', (ctx) => {
ctx.body = `Update user ${ctx.params.id}`;
})
.delete('/users/:id', (ctx) => {
ctx.body = `Delete user ${ctx.params.id}`;
});
// 或者使用all方法处理所有HTTP方法
router.all('/users', (ctx) => {
ctx.body = 'Handle all methods for /users';
});
路由参数
路径参数
javascript
// 单个参数
router.get('/users/:id', (ctx) => {
ctx.body = `User ID: ${ctx.params.id}`;
});
// 多个参数
router.get('/users/:userId/posts/:postId', (ctx) => {
ctx.body = `User: ${ctx.params.userId}, Post: ${ctx.params.postId}`;
});
// 可选参数
router.get('/users/:id?', (ctx) => {
if (ctx.params.id) {
ctx.body = `User ID: ${ctx.params.id}`;
} else {
ctx.body = 'All users';
}
});
// 带修饰符的参数
router.get('/users/:id{[0-9]+}', (ctx) => {
// 只匹配数字ID
ctx.body = `User ID: ${ctx.params.id}`;
});
// 零个或多个参数
router.get('/files/*', (ctx) => {
ctx.body = `File path: ${ctx.params[0]}`;
});
查询参数
javascript
router.get('/search', (ctx) => {
const { q, page = 1, limit = 10, sort = 'createdAt' } = ctx.query;
ctx.body = {
query: q,
pagination: {
page: parseInt(page),
limit: parseInt(limit)
},
sort
};
});
// 查询字符串
router.get('/api', (ctx) => {
ctx.body = {
query: ctx.query, // 解析后的查询参数对象
queryString: ctx.querystring // 原始查询字符串
};
});
路由前缀和嵌套
路由前缀
javascript
// 为一组路由设置公共前缀
const apiRouter = new Router({ prefix: '/api/v1' });
apiRouter.get('/users', (ctx) => {
ctx.body = 'API: Get all users';
});
apiRouter.get('/posts', (ctx) => {
ctx.body = 'API: Get all posts';
});
app.use(apiRouter.routes());
路由嵌套
javascript
const userRouter = new Router();
const postRouter = new Router();
// 用户相关路由
userRouter.get('/', (ctx) => { ctx.body = 'Get all users'; });
userRouter.get('/:id', (ctx) => { ctx.body = `Get user ${ctx.params.id}`; });
// 帖子相关路由
postRouter.get('/', (ctx) => { ctx.body = 'Get all posts'; });
postRouter.get('/:id', (ctx) => { ctx.body = `Get post ${ctx.params.id}`; });
// 主路由
const mainRouter = new Router();
mainRouter.use('/users', userRouter.routes());
mainRouter.use('/posts', postRouter.routes());
app.use(mainRouter.routes());
路由中间件
路由级中间件
javascript
// 认证中间件
const auth = async (ctx, next) => {
const token = ctx.headers.authorization;
if (!token) {
ctx.status = 401;
ctx.body = { error: 'Unauthorized' };
return;
}
await next();
};
// 为特定路由应用中间件
router.get('/protected', auth, (ctx) => {
ctx.body = 'This is protected content';
});
// 为多个路由应用中间件
const apiRoutes = new Router();
apiRoutes.use(auth); // 应用到所有路由
apiRoutes.get('/profile', (ctx) => { ctx.body = 'User profile'; });
apiRoutes.get('/settings', (ctx) => { ctx.body = 'User settings'; });
参数验证中间件
javascript
// 参数验证中间件
const validateUserId = async (ctx, next) => {
const { id } = ctx.params;
if (!id || isNaN(id)) {
ctx.status = 400;
ctx.body = { error: 'Invalid user ID' };
return;
}
await next();
};
router.get('/users/:id', validateUserId, (ctx) => {
ctx.body = `User ID: ${ctx.params.id}`;
});
// 使用router.param为特定参数添加验证
router.param('userId', async (id, ctx, next) => {
if (!/^[0-9a-fA-F]{24}$/.test(id)) {
ctx.status = 400;
ctx.body = { error: 'Invalid user ID format' };
return;
}
await next();
});
router.get('/users/:userId', (ctx) => {
ctx.body = `Valid user ID: ${ctx.params.userId}`;
});
路由分组和模块化
路由模块化
// routes/users.js
const Router = require('@koa/router');
const router = new Router({ prefix: '/users' });
// 用户验证中间件
const validateUser = async (ctx, next) => {
// 验证逻辑
await next();
};
router.get('/', async (ctx) => {
ctx.body = await getUserList();
});
router.get('/:id', async (ctx) => {
ctx.body = await getUserById(ctx.params.id);
});
router.post('/', validateUser, async (ctx) => {
ctx.body = await createUser(ctx.request.body);
});
router.put('/:id', validateUser, async (ctx) => {
ctx.body = await updateUser(ctx.params.id, ctx.request.body);
});
router.delete('/:id', async (ctx) => {
ctx.body = await deleteUser(ctx.params.id);
});
module.exports = router;
// app.js
const userRouter = require('./routes/users');
app.use(userRouter.routes());
路由权限控制
// 权限中间件
const requireRole = (role) => {
return async (ctx, next) => {
if (!ctx.state.user || ctx.state.user.role !== role) {
ctx.status = 403;
ctx.body = { error: 'Insufficient permissions' };
return;
}
await next();
};
};
// 应用权限控制
router.get('/admin', requireRole('admin'), (ctx) => {
ctx.body = 'Admin panel';
});
router.get('/moderator', requireRole('moderator'), (ctx) => {
ctx.body = 'Moderator panel';
});
高级路由功能
正则表达式路由
// 使用正则表达式定义路由
router.get(/^\/users\/(\d+)$/, (ctx) => {
// ctx.params[0] 包含匹配的数字
ctx.body = `User ID: ${ctx.params[0]}`;
});
// 复杂的正则表达式路由
router.get(/^\/files\/(images|videos)\/(.*)/, (ctx) => {
const [type, filename] = ctx.params;
ctx.body = `Type: ${type}, File: ${filename}`;
});
嵌套路由参数
// 嵌套资源路由
const router = new Router();
// 用户的帖子
router.get('/users/:userId/posts', (ctx) => {
ctx.body = `Posts for user ${ctx.params.userId}`;
});
// 用户的特定帖子
router.get('/users/:userId/posts/:postId', (ctx) => {
ctx.body = `Post ${ctx.params.postId} for user ${ctx.params.userId}`;
});
// 用户的帖子的评论
router.get('/users/:userId/posts/:postId/comments', (ctx) => {
ctx.body = `Comments for post ${ctx.params.postId} of user ${ctx.params.userId}`;
});
路由错误处理
404处理
// 404中间件应该在路由之后
app.use(router.routes());
app.use(async (ctx, next) => {
if (ctx.status === 404) {
ctx.body = { error: 'Route not found' };
ctx.status = 404;
}
await next();
});
路由特定错误处理
// 路由级别的错误处理
router.get('/users/:id', async (ctx) => {
try {
const user = await findUser(ctx.params.id);
if (!user) {
ctx.status = 404;
ctx.body = { error: 'User not found' };
return;
}
ctx.body = user;
} catch (err) {
ctx.status = 500;
ctx.body = { error: 'Internal server error' };
}
});
路由性能优化
路由缓存
// 简单的路由响应缓存
const routeCache = new Map();
const cachedRoute = (ttl = 60) => {
return async (ctx, next) => {
const key = `${ctx.method}:${ctx.path}`;
const cached = routeCache.get(key);
if (cached && Date.now() - cached.timestamp < ttl * 1000) {
ctx.body = cached.body;
ctx.status = cached.status;
return;
}
await next();
// 只缓存成功响应
if (ctx.status >= 200 && ctx.status < 300) {
routeCache.set(key, {
body: ctx.body,
status: ctx.status,
timestamp: Date.now()
});
}
};
};
router.get('/expensive-operation', cachedRoute(300), async (ctx) => {
// 耗时操作
ctx.body = await performExpensiveOperation();
});
路由分组优化
// 将相关路由组织在一起以提高性能
const apiRouter = new Router({ prefix: '/api' });
// 批量定义相似路由
const defineCRUDRoutes = (router, resource, controller) => {
router.get(`/${resource}`, controller.list);
router.get(`/${resource}/:id`, controller.get);
router.post(`/${resource}`, controller.create);
router.put(`/${resource}/:id`, controller.update);
router.delete(`/${resource}/:id`, controller.delete);
};
defineCRUDRoutes(apiRouter, 'users', userController);
defineCRUDRoutes(apiRouter, 'posts', postController);
app.use(apiRouter.routes());
路由最佳实践
1. RESTful路由设计
// 遵循RESTful设计原则
const resourceRouter = new Router();
// GET /resource - 获取资源列表
resourceRouter.get('/', listResources);
// GET /resource/:id - 获取单个资源
resourceRouter.get('/:id', getResource);
// POST /resource - 创建资源
resourceRouter.post('/', createResource);
// PUT /resource/:id - 更新资源
resourceRouter.put('/:id', updateResource);
// DELETE /resource/:id - 删除资源
resourceRouter.delete('/:id', deleteResource);
// GET /resource/:id/related - 获取相关资源
resourceRouter.get('/:id/related', getRelatedResources);
2. 路由验证
// 输入验证
const validateInput = (schema) => {
return async (ctx, next) => {
try {
const validatedData = await schema.validateAsync(ctx.request.body);
ctx.validatedBody = validatedData;
await next();
} catch (error) {
ctx.status = 400;
ctx.body = { error: error.details[0].message };
}
};
};
// 使用验证中间件
const userSchema = {
// Joi或其他验证库的模式
};
router.post('/users', validateInput(userSchema), (ctx) => {
ctx.body = ctx.validatedBody;
});
通过合理的路由管理,可以构建结构清晰、易于维护的Koa应用。