主题
中间件
介绍
一句话理解:
中间件是在请求和响应之间加一层"拦截器",在请求到达业务逻辑之前做一些事,或在响应返回给客户端之前做一些事。
核心思想:不改业务逻辑本身,通过插件化在两端做处理。
生命周期图示
请求进来
↓
[中间件1] ← 可以检查、修改请求
↓
[中间件2] ← 可以记日志、做权限校验
↓
[中间件3] ← 可以做格式转换
↓
业务逻辑处理
↓
[中间件3] ← 可以修改响应
↓
[中间件2] ← 可以记日志
↓
[中间件1] ← 最后一道门
↓
响应给客户端中间件通常是洋葱圈模型:进来的顺序和出去的顺序相反。
常见中间件类型
1. 认证/授权中间件(Authentication & Authorization)
作用:检查用户身份和权限。
常见用途:
- 验证 JWT/Session 是否有效
- 检查用户是否登录
- 检查用户是否有某个操作的权限
示例(Express 伪代码):
javascript
app.use((req, res, next) => {
const token = req.headers.authorization
if (!token) {
return res.status(401).json({ error: 'No token' })
}
// 验证 token...
next() // 继续下一个中间件
})2. 日志中间件(Logging)
作用:记录请求和响应的信息。
常见用途:
- 记录请求 URL、方法、响应时间
- 用于性能监控和问题排查
- 审计敏感操作
实战框架:
- Node.js:morgan、winston
- Java:Spring 的 Filter、拦截器
- Python:Flask 的 before_request、after_request
3. CORS 中间件(跨域资源共享)
作用:允许或拒绝来自不同源的请求。
为什么需要:浏览器有同源策略,跨域请求默认被拒绝。
常见框架:
- Express:
cors包 - Python Flask:
flask-cors - Java Spring:
@CrossOrigin
示例(Express):
javascript
const cors = require('cors')
app.use(cors())4. 请求体解析中间件(Body Parser)
作用:解析请求体(如 JSON、表单数据),转换成对象。
常见用途:
- 解析 Content-Type: application/json
- 解析表单数据
- 解析文件上传
常见框架:
- Express:
body-parser或内置express.json() - Python Flask:
request.get_json() - Java Spring:自动处理
5. 错误处理中间件(Error Handling)
作用:捕获应用内的错误,统一返回给客户端。
常见用途:
- 捕获未处理的异常
- 返回统一的错误格式
- 避免返回技术细节给用户
示例(Express 的错误处理中间件一般在最后):
javascript
app.use((err, req, res, next) => {
console.error(err)
res.status(500).json({
code: 'INTERNAL_ERROR',
message: 'Something went wrong',
})
})6. 速率限制中间件(Rate Limiting)
作用:限制单位时间内的请求数,防止滥用。
常见用途:
- 防止 API 被恶意调用
- 防止暴力破解(登录尝试次数限制)
- 保护服务器资源
常见框架:
- Express:
express-rate-limit - Python Flask:
Flask-Limiter - Nginx:原生支持
示例(Express):
javascript
const rateLimit = require('express-rate-limit')
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分钟
max: 100, // 最多 100 个请求
})
app.use(limiter)7. 数据验证中间件(Validation)
作用:验证请求数据是否符合预期格式。
常见用途:
- 检查必填字段
- 验证数据类型、长度、格式
- 提前发现不合法的请求
常见库:
- Joi、yup、zod(通用验证库)
- class-validator(TypeScript)
- Pydantic(Python)
8. 压缩中间件(Compression)
作用:压缩响应体,减少传输大小。
常见用途:
- 使用 gzip 压缩 HTML、JSON、CSS、JS
- 减少带宽消耗
- 加快加载速度
常见框架:
- Express:
compression包 - Nginx:内置 gzip 支持
- Python Flask:
Flask-Compress
9. 缓存中间件(Caching)
作用:缓存响应,避免重复计算或数据库查询。
常见用途:
- 缓存 GET 请求的响应
- 缓存数据库查询结果
- 设置 Cache-Control 响应头
常见方案:
- 内存缓存(Redis)
- HTTP 缓存(ETag、Last-Modified)
- CDN 缓存
10. 请求转换中间件(Request Transformation)
作用:修改或扩展请求对象。
常见用途:
- 添加用户信息到 request 对象
- 格式化查询参数
- 添加请求 ID 用于追踪
示例:
javascript
app.use((req, res, next) => {
req.userId = 123 // 从 token 解析得到
req.requestId = generateId()
next()
})11. 安全中间件(Security)
作用:增强应用安全性。
常见用途:
- 设置安全相关的响应头(如 X-Frame-Options、X-XSS-Protection)
- HTTPS 重定向
- CSRF 保护
- SQL 注入防护
常见库:
- Express:
helmet - Python Flask:
Flask-Security
示例(Express):
javascript
const helmet = require('helmet')
app.use(helmet())Web 框架中的中间件对比
| 框架 | 中间件术语 | 调用方式 |
|---|---|---|
| Express | Middleware | app.use() |
| Koa | Middleware | app.use() |
| Django | Middleware | 在 settings.py 配置 |
| Flask | 装饰器 / before_request | @app.before_request |
| Spring | Interceptor / Filter | 注册为 Bean |
| FastAPI | Middleware / Dependency | @app.middleware() |
中间件编写的通用步骤
Express 示例
javascript
// 简单的中间件
function myMiddleware(req, res, next) {
console.log('请求进来了:', req.url)
next() // 继续下一个中间件
}
app.use(myMiddleware)
// 带条件的中间件
function checkAdmin(req, res, next) {
if (req.user.role === 'admin') {
next()
} else {
res.status(403).json({ error: 'Forbidden' })
}
}
app.delete('/admin/users', checkAdmin, (req, res) => {
// 业务逻辑...
})Koa 示例(async/await)
javascript
app.use(async (ctx, next) => {
console.log('请求进来了:', ctx.url)
await next() // 继续下一个中间件
console.log('响应即将返回')
})中间件执行顺序
非常重要:中间件的执行顺序是按注册顺序。
javascript
app.use(authMiddleware) // 第一个执行
app.use(loggingMiddleware) // 第二个执行
app.use(validationMiddleware) // 第三个执行
app.get('/api/data', handler)如果你的认证中间件在最后,前面的中间件就无法访问用户信息。
实战建议
推荐的中间件加载顺序
1. 日志中间件 (记录所有请求)
2. 安全中间件(helmet) (设置安全头)
3. CORS 中间件 (跨域处理)
4. 请求体解析 (body-parser)
5. 认证中间件 (验证身份)
6. 速率限制 (防止滥用)
7. 数据验证 (验证请求数据)
8. 压缩中间件 (压缩响应)
9. 业务路由
10. 404 处理
11. 错误处理中间件 (捕获异常)常见坑
忘记调用 next()
会导致后续中间件和路由都不执行。中间件顺序不对
认证中间件在解析 body 之前,会拿不到请求体。同步中间件中有异步操作
使用 async/await 或 Promise,不要忽略错误。在响应已发送后还调用 next()
会导致 "headers already sent" 错误。
补充:广义的"中间件"
这里讲的是 Web 框架内的中间件。但"中间件"这个词在更广的范围内还有其他含义。
系统级中间件(Infrastructure)
这些通常指独立的软件或服务:
| 名称 | 分类 | 用途 |
|---|---|---|
| Redis | 内存存储 | 缓存、会话、消息队列 |
| RabbitMQ | 消息队列 | 异步任务、服务解耦 |
| Kafka | 流消息平台 | 日志收集、事件处理 |
| MySQL / MongoDB | 数据库 | 数据持久化 |
| Consul / Nacos | 服务注册中心 | 服务发现、配置管理 |
| Nginx | 反向代理 | 负载均衡、请求转发 |
| ElasticSearch | 搜索引擎 | 日志搜索、全文搜索 |
这些不是框架中间件,而是应用依赖的基础设施。
在应用中使用它们
比如在 Express 中访问 Redis,看起来像这样:
javascript
// 框架中间件 + 系统中间件的组合
app.use((req, res, next) => {
const userId = req.user.id
// 调用 Redis(系统中间件)
redis.get(`user:${userId}`, (err, data) => {
req.cachedUser = data
next() // 继续框架流程
})
})
app.get('/user', (req, res) => {
res.json(req.cachedUser)
})这里:
- 框架中间件:
app.use()的函数,拦截请求 - 系统中间件:Redis 客户端,存取数据
区别
- Web 框架中间件:在单个请求内执行,处理 HTTP 流程
- 系统中间件:独立服务,应用与它们通信,生命周期独立
两者不互相替代,而是配合使用。
总结
- 中间件本质是请求/响应的拦截器。
- 可以做认证、日志、验证、转换等各种事。
- 框架不同,术语和用法有差异,但核心思想一样。
- 执行顺序很重要,要根据依赖关系合理排列。
- 写中间件时记得调用
next()继续链条。 - 别把"Web 框架中间件"和"系统中间件"(Redis、RabbitMQ 等)混淆。
掌握中间件,就能写出整洁、可维护的代码,避免业务逻辑里混杂各种横切关注点(日志、认证等)。
