🌟 为什么要学 Koa 路由?
在我们开始写代码之前,先来想一个问题:用户访问一个网页时,服务器是怎么知道该返回哪个页面的?
答案就是:路由系统。它是 Web 服务器的“导航员”,负责将用户请求引导到正确的处理逻辑上。
在 Koa 中,路由系统不是内置的,我们需要借助一个插件:@koa/router。这篇文章会带你从零开始,理解 Koa 路由的原理、用法和最佳实践。
一、什么是路由?它为什么重要?
1.1 路由就像游乐园的地图
想象你去游乐园玩,里面有各种项目:过山车、旋转木马、水上滑梯……如果你没有地图,你可能会迷路。而地图的作用就是告诉你:去哪个项目,该走哪条路。
在 Web 应用中,路由(Routing)就是这张“地图”。它负责:
- 根据用户访问的网址(比如
/login、/profile) - 判断用户使用的是哪种请求方式(GET、POST 等)
- 把请求交给对应的处理函数
- 返回响应(比如一段文字、一张图片、一个 JSON 数据)
1.2 路由的基本结构
一个路由通常包含三个部分:
| 组成部分 | 说明 |
|---|---|
| 请求方法 | GET、POST、PUT、DELETE 等 |
| 请求路径 | 比如 /login、/users/123 |
| 处理函数 | 接收请求、处理逻辑、返回结果 |
二、Koa 中的路由系统基础
2.1 为什么需要安装 @koa/router?
Koa 本身只提供了最基础的 HTTP 服务功能,并没有内置完整的路由功能。所以我们要安装一个插件:@koa/router,来帮助我们管理路由。
2.2 安装依赖
在使用 Koa 路由之前,先安装必要的依赖:
npm install koa @koa/router
如果你还需要处理 POST 请求中的数据,还需要安装:
npm install @koa/bodyparser
2.3 创建一个最简单的路由示例
const Koa = require('koa')
const Router = require('@koa/router')
const bodyParser = require('@koa/bodyparser')
const app = new Koa()
const router = new Router()
// 使用 bodyparser 解析 POST 请求体
app.use(bodyParser())
// 定义一个 GET 请求的路由
router.get('/', (ctx) => {
ctx.body = '欢迎来到首页!'
})
// 注册路由到 Koa 应用中
app.use(router.routes())
// 启动服务器
app.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000')
})
这段代码首先创建了一个 Koa 应用实例,并引入了路由模块 @koa/router,然后创建了一个路由实例。接着,使用 bodyParser 中间件来解析 POST 请求中的数据。之后定义了一个 GET 请求的路由,当访问根路径 / 时,返回“欢迎来到首页!”的响应内容。将定义好的路由注册到 Koa 应用中后,启动服务器,监听 3000 端口。运行成功后,在浏览器中访问 http://localhost:3000,即可看到返回的响应内容。
三、路由的常见用法
3.1 为什么需要“路由前缀”?
假设你正在开发一个用户管理模块,所有的用户接口都以 /user 开头,比如:
/user/login/user/profile/user/logout
如果每个路由都手动加上 /user,不仅麻烦,而且容易出错。这时候我们就可以使用 路由前缀 来统一管理。
✅ 如何设置路由前缀?
const userRouter = new Router()
userRouter.prefix('/user') // 设置前缀为 /user
userRouter.get('/login', (ctx) => {
ctx.body = '这是登录接口'
})
userRouter.get('/:id', (ctx) => {
const userId = ctx.params.id
ctx.body = `这是用户 ID 为 ${userId} 的详情页`
})
app.use(userRouter.routes())
现在访问 /user/login 和 /user/123,就会分别进入对应的接口。
3.2 为什么需要 GET 和 POST 请求?
HTTP 协议定义了多种请求方法,其中最常用的是:
- GET:用于获取数据,参数在 URL 上。当你在浏览器中输入网址、点击超链接、或者访问一个搜索页面时,浏览器默认发送的就是一个 GET 请求。参数可见,不安全,不适合传递敏感信息(如密码)
- POST:用于提交数据,参数在请求体中。用于向服务器提交数据,比如用户注册、登录、提交表单等。与 GET 不同,POST 请求的数据是通过请求体(body)发送的。
✅ GET 请求怎么用?
router.get('/search', (ctx) => {
const keyword = ctx.query.keyword
ctx.body = `你搜索的关键词是:${keyword}`
})
访问 /search?keyword=hello,会返回:
你搜索的关键词是:hello
✅ POST 请求怎么用?
POST 请求的数据是通过请求体(body)传过来的,所以我们需要使用 @koa/bodyparser 插件。
app.use(bodyParser())
router.post('/login', (ctx) => {
const { username, password } = ctx.request.body
ctx.body = `你输入的用户名是:${username},密码是:${password}`
})
注意:POST 请求不能直接在浏览器地址栏测试,建议使用 Postman 或前端页面来发送请求。
四、如何组织你的路由文件?
4.1 为什么要把路由拆分成多个文件?
随着项目越来越大,我们不可能把所有路由都写在一个文件里。这样做会导致:
- 代码臃肿,难以维护
- 路由之间容易冲突
- 不利于多人协作
所以,我们需要把路由模块化。
✅ 如何拆分路由文件?
创建一个 routes/user.js 文件:
const Router = require('@koa/router')
const router = new Router()
router.get('/', (ctx) => {
ctx.body = '用户首页'
})
router.get('/:id', (ctx) => {
ctx.body = `用户 ID 是:${ctx.params.id}`
})
module.exports = router
然后在主文件中引入:
const userRouter = require('./routes/user')
app.use(userRouter.routes())
这样可以让项目结构更清晰,也方便维护。
五、最佳实践建议
5.1 为什么要进行参数验证?
用户的输入是不可控的。比如注册时,用户可能不填邮箱或密码,这时候如果不做验证,程序可能会出错。
router.post('/register', (ctx) => {
const { email, password } = ctx.request.body
if (!email || !password) {
ctx.status = 400
ctx.body = { error: '邮箱和密码不能为空' }
return
}
// 继续注册逻辑...
})
5.2 为什么要使用 RESTful 风格?
RESTful 是一种设计 API 的规范,它让接口看起来更清晰、更统一。常见设计如下:
| 方法 | 路径 | 作用 |
|---|---|---|
| GET | /users | 获取所有用户 |
| POST | /users | 创建新用户 |
| GET | /users/:id | 获取某个用户 |
| PUT | /users/:id | 更新某个用户 |
| DELETE | /users/:id | 删除某个用户 |
5.3 为什么要使用错误处理中间件?
程序总会出错,比如数据库连接失败、参数错误、权限不足等。我们可以使用一个全局的错误处理中间件来统一处理这些异常。
app.use(async (ctx, next) => {
try {
await next()
} catch (err) {
ctx.status = err.status || 500
ctx.body = { error: err.message }
}
})
这样即使程序出错,用户也能看到友好的错误提示,而不是直接崩溃。
六、开发工具推荐
每次修改代码后都要手动重启服务器很麻烦。我们可以使用 nodemon 来实现自动重启。
npm install -g nodemon
然后用它启动你的服务器:
nodemon index.js
这样每次保存代码后,服务会自动重启,节省大量时间。