Express 路由:请求与响应之间的映射
路由是 Web 应用的导航系统。它决定了当用户发送一个请求时,服务器应该如何响应。
这个概念很简单:用户访问一个 URL,服务器返回对应的内容。但正是这种简单性,让路由成为构建 Web 应用最核心的机制之一。
路由是一种映射关系
本质上,路由就是建立 URL 和处理函数之间的映射。
app.get('/users', (req, res) => {
res.send('用户列表')
})
这行代码说:当有人访问 /users 时,执行这个函数。就这么简单。
💡 Click the maximize icon to view in fullscreen
路由系统做的事情不复杂:接收请求,找到对应的函数,执行它。但这个简单的机制支撑了整个 Web 应用的运转。
为什么需要明确的映射
没有路由,服务器就是一个黑盒。用户发送请求,却不知道会得到什么。路由让这个过程变得可预测。
app.get('/users/:id', (req, res) => {
const userId = req.params.id
res.send(`用户 ${userId} 的信息`)
})
路由参数让 URL 变得动态。/users/1 和 /users/2 是不同的请求,但它们共享同一个处理逻辑。这种模式很常见:URL 表达意图,参数表达细节。
路由让服务器的行为变得明确。开发者定义规则,用户遵循规则,系统就能运转。
不同的 HTTP 方法代表不同的意图
Express 路由不只是处理 URL,还要处理 HTTP 方法。同样的 URL,不同的方法,意味着不同的操作。
app.get('/users', (req, res) => {
// 获取用户列表
})
app.post('/users', (req, res) => {
// 创建新用户
})
app.put('/users/:id', (req, res) => {
// 更新用户信息
})
app.delete('/users/:id', (req, res) => {
// 删除用户
})
这就是 RESTful 设计的基础。URL 表示资源,HTTP 方法表示操作。GET 是读取,POST 是创建,PUT 是更新,DELETE 是删除。
HTTP 方法不只是技术约定,它们是语义层面的设计。选择正确的方法能让 API 更容易理解和使用。
GET:安全且幂等,不改变服务器状态POST:创建新资源,不是幂等的PUT:更新整个资源,幂等PATCH:更新部分资源DELETE:删除资源,幂等
幂等性很重要。如果一个请求失败了,客户端可以安全地重试幂等操作,而不用担心产生副作用。
中间件让路由变得可组合
Express 的强大之处在于中间件。路由不只是简单的映射,它是一个处理链。
const authenticate = (req, res, next) => {
if (req.headers.authorization) {
next()
} else {
res.status(401).send('未授权')
}
}
app.get('/dashboard', authenticate, (req, res) => {
res.send('仪表板内容')
})
authenticate 是一个中间件。它在路由处理函数之前运行,决定是否继续执行。如果验证失败,请求就在这里终止。
💡 Click the maximize icon to view in fullscreen
中间件让功能可以组合。认证、日志、错误处理——这些横切关注点不需要在每个路由里重复。它们是独立的函数,可以按需组合。
路由分组让代码更清晰
当应用变大,路由也会增多。把所有路由写在一起会变得混乱。Express 提供了 Router 来组织路由。
// users.js
const express = require('express')
const router = express.Router()
router.get('/', (req, res) => {
res.send('用户列表')
})
router.get('/:id', (req, res) => {
res.send(`用户 ${req.params.id}`)
})
module.exports = router
// app.js
const usersRouter = require('./users')
app.use('/users', usersRouter)
这样,所有用户相关的路由都在 users.js 里。主文件保持简洁,每个模块专注于自己的职责。
路由分组不只是组织代码,它也反映了系统的逻辑结构。/users 是一个资源,/posts 是另一个资源,它们各自独立。
错误处理是路由设计的一部分
路由不只是处理成功的情况,还要处理失败。Express 的错误处理中间件专门用来捕获错误。
app.get('/users/:id', async (req, res, next) => {
try {
const user = await getUserById(req.params.id)
res.send(user)
} catch (error) {
next(error)
}
})
app.use((err, req, res, next) => {
console.error(err)
res.status(500).send('服务器错误')
})
next(error) 把错误传递给错误处理中间件。这种机制让错误处理集中化,不需要在每个路由里写 try-catch。
Express 的错误处理中间件必须放在所有路由之后。顺序很重要:
// 先定义路由
app.get('/users', handler)
// 最后定义错误处理
app.use((err, req, res, next) => {
// 处理错误
})
如果把错误处理放在前面,它就捕获不到后面路由的错误。
路由的性能考虑
路由匹配是有成本的。Express 按顺序检查每个路由,直到找到匹配的。
app.get('/users/:id', handler1)
app.get('/users/new', handler2)
这个顺序有问题。/users/new 会被第一个路由匹配,因为 :id 可以匹配任何值。正确的做法是把更具体的路由放在前面:
app.get('/users/new', handler2)
app.get('/users/:id', handler1)
路由的顺序影响匹配逻辑。越具体的规则应该越早定义。
最后
路由是 Web 应用的骨架。它定义了服务器如何响应不同的请求,如何组织功能,如何处理错误。
Express 的路由设计很简洁:一个 URL,一个方法,一个处理函数。但这种简洁性支撑了无数复杂的应用。
理解路由不只是学习语法,而是理解服务器如何与外界交互。每个路由都是一个承诺:当你发送这个请求,我会给你这个响应。信任建立在这些承诺之上。
Related Posts
Articles you might also find interesting