路由系统详解:从入门到实战
路由,说白了就是API的「门牌号」。客户端发来请求,服务器得知道该找谁处理。我刚开始学Express时,觉得路由不就是个路径匹配嘛,后来踩过坑才明白——路由设计的好坏,直接决定了API的可维护性。
路由概念:请求的「导航系统」
每个HTTP请求都包含两个核心信息:路径(path)和方法(method)。路由的作用,就是根据这两个信息,把请求分发给对应的处理函数。
核心公式:路由 = 路径 + HTTP方法 + 处理函数
举个例子:
app.get('/users', (req, res) => {
res.send('获取用户列表');
});
这段代码的意思是:当客户端用GET方法访问/users路径时,执行后面的回调函数。嗯,就是这么简单。
HTTP方法:CRUD的「动作指令」
RESTful API中,不同的HTTP方法对应不同的操作。我个人习惯这样对应:
| HTTP方法 | 对应操作 | 典型场景 |
|---|---|---|
| GET | 查询 | 获取资源列表或详情 |
| POST | 创建 | 新增用户、提交订单 |
| PUT | 全量更新 | 替换整个资源 |
| DELETE | 删除 | 移除资源 |
你想想看,如果所有操作都用GET,那API就乱套了。我在项目中遇到过团队新人把所有请求都写成GET,结果删除操作也能被浏览器直接访问,那叫一个惊险。
避坑指南:我曾经见过一个项目,用GET方法做删除操作,结果搜索引擎爬虫把整个数据库删光了。记住:修改数据一定要用POST/PUT/DELETE,别偷懒。
路由参数:动态路径的「占位符」
实际开发中,路径往往是动态的。比如获取某个用户的详情,路径可能是/users/123,其中123是用户ID。这时候就需要路由参数。
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.send(`获取用户 ${userId} 的信息`);
});
:id就是路由参数,Express会自动解析并挂载到req.params上。我个人习惯给参数起有意义的名字,比如:userId、:orderId,而不是:a、:b——你想想看,三个月后回来看代码,谁还记得:a是啥?
还可以有多个参数:
app.get('/users/:userId/orders/:orderId', (req, res) => {
const { userId, orderId } = req.params;
res.send(`用户 ${userId} 的订单 ${orderId}`);
});
小技巧:路由参数默认是字符串。如果需要数字,记得用parseInt或Number转换。我曾经因为忘记转换,导致1 + 1 = '11'的诡异bug,排查了半天。
路由处理函数:请求的「终点站」
每个路由最终都要有一个处理函数。这个函数接收两个核心参数:req(请求对象)和res(响应对象)。
app.post('/users', (req, res) => {
// 1. 获取请求体数据
const { name, email } = req.body;
// 2. 业务逻辑处理
const newUser = { id: Date.now(), name, email };
// 3. 返回响应
res.status(201).json(newUser);
});
处理函数里一般做三件事:解析请求 → 处理业务 → 返回响应。我建议保持函数简短,如果逻辑复杂,就拆成多个中间件或单独的函数。
经验之谈:处理函数里不要写超过50行的代码。如果超过,说明该拆分了。我在重构一个老项目时,见过一个路由处理函数写了300行——那简直是灾难。
链式路由:优雅的「路由组织」
同一个路径,不同方法,如果分开写,代码会显得散乱。链式路由可以解决这个问题:
app.route('/users')
.get((req, res) => {
res.send('获取用户列表');
})
.post((req, res) => {
res.send('创建新用户');
})
.put((req, res) => {
res.send('批量更新用户');
})
.delete((req, res) => {
res.send('删除所有用户');
});
你看,这样是不是清晰多了?所有针对/users的操作都集中在一起。我个人非常喜欢这种写法,尤其是在写RESTful API时,一个资源对应一个route(),维护起来特别方便。
链式路由还可以配合中间件:
app.route('/users/:id')
.all((req, res, next) => {
// 这个中间件对所有方法生效
console.log(`访问用户 ${req.params.id}`);
next();
})
.get((req, res) => {
res.send(`获取用户 ${req.params.id}`);
})
.put((req, res) => {
res.send(`更新用户 ${req.params.id}`);
})
.delete((req, res) => {
res.send(`删除用户 ${req.params.id}`);
});
实用技巧:.all()方法可以捕获所有HTTP方法,适合做权限校验、日志记录等通用操作。我经常用它来做「这个资源是否存在」的预检查。
实战建议:路由设计原则
说了这么多,最后分享几个我在项目中总结的原则:
- 路径用名词复数:
/users而不是/user,/orders而不是/getOrder - 嵌套不要超过三层:
/users/:userId/orders可以,但/users/:userId/orders/:orderId/items/:itemId就太深了 - 版本号放路径里:
/api/v1/users,方便后续升级 - 路由参数要有意义:别用
:a、:b这种让人猜谜的命名
嗯,路由系统就讲到这里。说白了,路由就是API的「交通指挥员」,设计好了,整个项目都清爽。下一章我们聊聊中间件——那才是Express真正的灵魂。