Express 子路由、静态资源与错误处理详解
2025年6月30日 18:12
在实际开发中,随着项目规模扩大,路由和中间件的数量会迅速增长。如果所有路由都写在同一个文件里,代码将变得难以维护。Express 提供了子路由(Router)和中间件机制,可以帮助我们实现模块化开发、静态资源托管和高效的错误处理。下面详细介绍这些内容。
一、子路由(Router)
1. 为什么要使用子路由?
当路由数量较多时,推荐将不同业务模块的路由拆分到不同文件或目录(如 router
或 routes
文件夹),每个模块对应一个子路由。这样可以做到高内聚、低耦合,提升项目可维护性和可扩展性。
2. express.Router 的原理
express.Router()
本质上是一个“迷你版”的 Express 应用,拥有完整的中间件和路由系统。你可以为它单独定义路由、中间件、参数处理等,最后将其挂载到主应用的某个路径下。
3. 子路由的使用示例
import express from 'express'
const app = express()
// 创建子路由对象
const userRouter = express.Router()
// 定义子路由
userRouter.get('/list', (req, res) => {
// req.url 是子路由基准路径之后的部分
// req.baseUrl 是子路由的基准路径
// req.originalUrl 是原始请求路径
res.json({
url: req.url,
path: req.path,
baseUrl: req.baseUrl,
originalUrl: req.originalUrl,
params: req.params,
query: req.query
})
})
userRouter.post('/create', (req, res) => res.end(req.url))
userRouter.patch('/:id', (req, res) => res.end(req.url))
// 挂载子路由到主应用
app.use('/users', userRouter)
app.listen(3000)
小结:
- 子路由路径必须以
/
开头。 - 请求到
/users/list
时,userRouter
内的/list
路由会被匹配。 - 这样拆分后,主文件只需负责路由挂载,具体逻辑分散在各自模块,结构清晰。
二、静态资源托管
Web 项目通常需要对外暴露静态文件(如图片、CSS、JS、上传文件等)。Express 提供了内置中间件 express.static()
,可将指定目录设为静态资源目录。
1. 使用方法
import express from 'express'
const app = express()
// 设置静态资源目录(可多次调用,支持多个目录)
app.use(express.static('./uploads'))
app.use(express.static('./build'))
app.listen(3000)
- 访问
http://localhost:3000/头像.jpg
,会自动查找uploads
和build
目录下的头像.jpg
文件。 - 静态资源中间件会优先返回第一个匹配到的文件。
建议:
将上传目录、前端打包目录等都设置为静态资源目录,便于统一管理和访问。
三、错误处理机制
后端服务不可避免会遇到各种异常情况。Express 提供了灵活的错误处理机制,主要包括两种方式:
1. HTTP 状态码 + 错误信息
这是最常见的处理方式。直接返回标准 HTTP 错误码(如 404、500),并在响应体中说明错误原因。
res.status(404).send('资源未找到')
- 优点:符合 HTTP 标准,前端可直接根据状态码判断。
- 缺点:有时无法满足复杂业务需求。
2. 业务自定义错误码
有些团队喜欢所有接口都返回 200 状态码,通过响应体中的自定义 code
字段区分业务成功/失败。
res.json({
code: 404,
message: '用户不存在',
data: null
})
- 优点:前后端约定灵活,业务错误粒度可控。
- 缺点:不符合 RESTful 风格,需前端配合解析。
3. 错误处理中间件
Express 允许通过特殊格式的中间件统一处理错误(即错误处理中间件)。只要在 next()
里传递参数,Express 就会跳过后续普通中间件,直接进入错误处理中间件。
import express from 'express'
const app = express()
app.use(express.json())
app.post('/login', (req, res, next) => {
const { name, password } = req.body
if (!name || !password) {
// 传递错误码到错误处理中间件
next(-1001)
} else {
res.send('登录成功')
}
})
// 错误处理中间件(参数比普通中间件多一个 err)
app.use((err, req, res, next) => {
switch (err) {
case -1001:
res.status(400).send('用户名或密码不能为空')
break
case -1002:
res.status(401).send('用户名或密码错误')
break
case -1003:
res.status(404).send('用户名不存在')
break
default:
res.status(500).send('服务器内部错误')
}
})
app.listen(3000)
建议:
- 错误处理中间件应放在所有路由之后。
- 可结合日志系统,记录错误详情,便于排查问题。
- 复杂项目可对错误码、错误信息进行统一管理。