跳到主要内容

洋葱模型

🧅 一、各主流框架的中间件模型对比表

框架语言是否使用洋葱模型控制调用方式特点说明
KoaJavaScript / Node.js✅ 是await next()典型的洋葱模型代表;每个中间件包裹下一层;支持 async/await;请求→响应双向流。
GinGo✅ 是c.Next()受 Koa 启发,实现同样的“进入再返回”逻辑;高性能、结构清晰。
HertzGo(字节跳动)✅ 是ctx.Next()类似 Gin 的洋葱模型设计,支持异步与链式中间件。
FastifyNode.js✅ 部分实现preHandler + onSend拦截链逻辑近似洋葱模型,但分阶段钩子化;非严格嵌套式。
ExpressNode.js❌ 否(线性模型)next()按注册顺序线性执行,无法等待下层返回;单向流。
FlaskPython❌ 否before_request / after_request请求钩子模型;非嵌套式、不可回溯。
Spring Boot (Spring MVC)Java⚠️ 类似(拦截器链)preHandle / postHandle请求前后调用链,与洋葱模型思想相似但实现更偏向 AOP。
ASP.NET CoreC#✅ 是await next(context)官方称为“Middleware Pipeline”,确实是洋葱模型结构。
DjangoPython⚠️ 类似(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#) 等框架广泛采用。