Skip to content

中间件

介绍

一句话理解:

中间件是在请求和响应之间加一层"拦截器",在请求到达业务逻辑之前做一些事,或在响应返回给客户端之前做一些事。

核心思想:不改业务逻辑本身,通过插件化在两端做处理

生命周期图示

请求进来

[中间件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 框架中的中间件对比

框架中间件术语调用方式
ExpressMiddlewareapp.use()
KoaMiddlewareapp.use()
DjangoMiddleware在 settings.py 配置
Flask装饰器 / before_request@app.before_request
SpringInterceptor / Filter注册为 Bean
FastAPIMiddleware / 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. 错误处理中间件    (捕获异常)

常见坑

  1. 忘记调用 next()
    会导致后续中间件和路由都不执行。

  2. 中间件顺序不对
    认证中间件在解析 body 之前,会拿不到请求体。

  3. 同步中间件中有异步操作
    使用 async/await 或 Promise,不要忽略错误。

  4. 在响应已发送后还调用 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 等)混淆。

掌握中间件,就能写出整洁、可维护的代码,避免业务逻辑里混杂各种横切关注点(日志、认证等)。

基于 MIT 许可发布