洋葱模型
🧅 一、各主流框架的中间件模型对比表
| 框架 | 语言 | 是否使用洋葱模型 | 控制调用方式 | 特点说明 |
|---|---|---|---|---|
| Koa | JavaScript / Node.js | ✅ 是 | await next() | 典型的洋葱模型代表;每个中间件包裹下一层;支持 async/await;请求→响应双向流。 |
| Gin | Go | ✅ 是 | c.Next() | 受 Koa 启发,实现同样的“进入再返回”逻辑;高性能、结构清晰。 |
| Hertz | Go(字节跳动) | ✅ 是 | ctx.Next() | 类似 Gin 的洋葱模型设计,支持异步与链式中间件。 |
| Fastify | Node.js | ✅ 部分实现 | preHandler + onSend | 拦截链逻辑近似洋葱模型,但分阶段钩子化;非严格嵌套式。 |
| Express | Node.js | ❌ 否(线性模型) | next() | 按注册顺序线性执行,无法等待下层返回;单向流。 |
| Flask | Python | ❌ 否 | before_request / after_request | 请求钩子模型;非嵌套式、不可回溯。 |
| Spring Boot (Spring MVC) | Java | ⚠️ 类似(拦截器链) | preHandle / postHandle | 请求前后调用链,与洋葱模型思想相似但实现更偏向 AOP。 |
| ASP.NET Core | C# | ✅ 是 | await next(context) | 官方称为“Middleware Pipeline”,确实是洋葱模型结构。 |
| Django | Python | ⚠️ 类似(Middleware Stack) | process_request / process_response | 串行栈结构,执行上有前后对称,但不是显式的嵌套调用。 |
二、🧩什么是“洋葱模型”
定义
“洋葱模型(Onion Model)”是一种中间件执行机制或系统架构思想,形象地描述了请求经过多层包裹处理(像穿过洋葱的层层外皮),再按相反顺序返回的过程。
请求:一层层向内执行(pre 逻辑)
响应:一层层向外返回(post 逻辑)
🌀 执行结构示意
┌──────────────────────┐
│ Middleware 1 │ ← 外层
│ ┌───────────────┐ │
│ │ Middleware 2 │ │
│ │ ┌─────────┐ │ │
│ │ │ Handler │ │ │ ← 内层核心逻辑
│ │ └─────────┘ │ │
│ └───────────────┘ │
└──────────────────────┘
执行顺序如下:
进入 M1 → 进入 M2 → 执行 Handler → 返回 M2 → 返回 M1
🧠 核心思想
| 阶段 | 动作 | 示例 |
|---|---|---|
| 请求阶段 | 做“前置处理” | 记录请求日志、验证权限、解析 Token |
| 响应阶段 | 做“后置处理” | 修改响应头、记录耗时、格式化输出 |
每个中间件既能控制进入时的逻辑,也能控制返回时的逻辑,这使得:
-
逻辑对称(前后都有)
-
可组合(层层包裹)
-
易扩展(新增中间件不会破坏原逻辑)
💡 举例(以 Koa 为例)
app.use(async (ctx, next) => {
console.log("进入外层");
await next();
console.log("返回外层");
});
app.use(async (ctx, next) => {
console.log("进入内层");
await next();
console.log("返回内层");
});
app.use(async ctx => {
console.log("处理请求");
ctx.body = "Hello";
});
输出结果:
进入外层
进入内层
处理请求
返回内层
返回外层
🧭 三、与线性模型的对比
| 模型 | 执行特征 | 代表框架 | 能否捕获下层异常 |
|---|---|---|---|
| 线性模型 | 顺序调用、不回溯 | Express、Flask | ❌ 不自然支持 |
| 洋葱模型 | 嵌套调用、先入后出 | Koa、Gin、ASP.NET Core | ✅ 可以用 try/catch 捕获 |
洋葱模型是一种“先进入、再返回”的中间件执行模式,
请求穿过多层中间件后到达核心逻辑,再逐层回溯处理响应。
它由 Koa(Node.js) 首次在主流框架中明确提出并普及,
并被 Gin(Go)、ASP.NET Core(C#) 等框架广泛采用。