路由
🎯 引言
学完这篇文章,你能给 Koa 应用装上路由,让 GET /users、POST /users 各司其职,拿到路径里的动态参数,还能把路由拆分到独立文件里管理。Koa 本身不带路由,这篇我们把它补齐。
🧱 安装 @koa/router
前面说过,Koa 核心非常精简,路由功能由官方的 @koa/router 库提供。本文基于 Node.js v22 LTS + @koa/router 15.7.0 编写和验证。
npm install @koa/router
基本用法分三步:创建路由实例、定义路由、把路由注册成中间件:
import Koa from 'koa';
import Router from '@koa/router';
const app = new Koa();
const router = new Router();
router.get('/', async (ctx) => {
ctx.body = '首页';
});
router.get('/about', async (ctx) => {
ctx.body = '关于页面';
});
// 把路由注册到应用上
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000);
现在访问 / 和 /about 会返回不同内容,访问其他地址会得到 404。router.get() 的写法和 Express 路由几乎一样,唯一的区别是回调参数从 (req, res) 换成了 (ctx)。
🛠 常用请求方法
RESTful 风格的接口需要区分请求方法,@koa/router 都支持:
router.get('/users', async (ctx) => {
ctx.body = '获取用户列表';
});
router.post('/users', async (ctx) => {
ctx.body = '创建用户';
});
router.put('/users/1', async (ctx) => {
ctx.body = '更新用户';
});
router.delete('/users/1', async (ctx) => {
ctx.body = '删除用户';
});
同一个路径配上不同方法,就是 RESTful 接口的经典形态。GET 和 DELETE 可以在浏览器或地址栏测试,POST 和 PUT 建议用 Apifox、Postman 这类接口工具来发请求。
🧱 路由参数与查询参数
路由参数是路径里的动态部分,用 :参数名 定义,通过 ctx.params 获取:
router.get('/users/:id', async (ctx) => {
const id = ctx.params.id; // 访问 /users/42 时,id 是 '42'
ctx.body = { id, name: '小明' };
});
查询参数是 ? 后面的部分,通过 ctx.query 获取(上一篇学过):
router.get('/users', async (ctx) => {
// 访问 /users?page=2&size=10
const { page, size } = ctx.query;
ctx.body = { page, size };
});
ctx.params 管路径里的(/users/:id),ctx.query 管问号后的(?page=2)。两者的值都是字符串,需要数字时记得转换。💡 allowedMethods 有什么用
app.use(router.allowedMethods()) 这行代码看着不起眼,作用有两个:
- 404 语义更准:路径存在但方法不对时(比如只定义了
GET /users,却发来了POST /users),返回 405 Method Not Allowed 而不是 404,告诉客户端“地址有,但你方法用错了”。 - 自动响应 OPTIONS 请求:浏览器跨域预检时发来 OPTIONS 请求,它会自动回复该路径支持哪些方法。
简单说,加上它,你的接口对“错误请求”的反馈会更规范,建议每次都带上。
🧰 Router 模块化拆分
所有路由都堆在 app.js 里,项目一大就没法维护了。实际开发会把不同模块的路由拆到独立文件:
import Router from '@koa/router';
const router = new Router({
prefix: '/users', // 路由前缀,下面的路径都会自动加上 /users
});
router.get('/', async (ctx) => {
ctx.body = '用户列表';
});
router.get('/:id', async (ctx) => {
ctx.body = `用户详情:${ctx.params.id}`;
});
export default router;
在主文件里引入并注册:
import Koa from 'koa';
import usersRouter from './routes/users.js';
const app = new Koa();
app.use(usersRouter.routes());
app.use(usersRouter.allowedMethods());
app.listen(3000);
注意 prefix: '/users' 配合 router.get('/'),最终匹配的路径是 /users;router.get('/:id') 匹配的是 /users/1 这样的地址。每个业务模块一个路由文件,结构清晰,和 Express 里 express.Router() 的拆分思路完全一致。
🧾 小节总结
- Koa 不带路由,需安装官方库
@koa/router,创建实例后用router.get()等方法定义路由。 - 路由通过
app.use(router.routes())注册,建议同时加上router.allowedMethods()。 - 路由参数
:id用ctx.params取,查询参数?page=2用ctx.query取,值都是字符串。 allowedMethods()让方法不匹配时返回 405,并自动处理 OPTIONS 请求。- 用
new Router({ prefix })加分文件导出,实现路由模块化拆分。
❓ 知识问答
Q1:路由定义的先后顺序有影响吗?
有。@koa/router 按注册顺序匹配,先匹配到的先生效。比如 /users/new 要定义在 /users/:id 前面,否则 new 会被当成 id 参数匹配走。
Q2:一个路由能同时处理多种方法吗?
可以,但一般不这么写。按 RESTful 规范,每个方法各定义一条路由,逻辑分开更清晰。
Q3:ctx.params.id 是数字吗?
不是,是字符串。路径本来就是文本,取到后需要数字时用 Number(ctx.params.id) 转换,记得处理 NaN 的情况。
Q4:忘记注册 allowedMethods 会怎样?
功能上接口照常工作,只是方法不匹配时统一返回 404 而不是 405,也无法自动响应 OPTIONS 请求。建议养成两个都注册的习惯。
🧪 小练习
- 新建路由文件
routes/articles.js,定义两个接口:GET /articles返回文章列表(假数据即可),GET /articles/:id返回指定 id 的文章,并在app.js中注册。
import Router from '@koa/router';
const router = new Router({ prefix: '/articles' });
// 请在这里编写代码
export default router;
- 分别用浏览器访问
/articles和/articles/1,确认两个路由都能正确响应。
🎉 恭喜你已经给 Koa 装上了路由!下一篇我们处理两个实际问题:解析前端发来的请求体数据,以及优雅地处理接口错误。
