全书目录

第三十六篇:Koa 完全指南 —— 洋葱关卡巡防宇宙

第三章:Koa 的灵魂 —— 洋葱模型到底在干什么

1 分钟 302 字 第 686 / 962 个阅读单元

Koa 最重要的,不是 ctx.body,而是它的 middleware onion model

你可以把一次请求想成一艘飞船穿过多层巡防关卡:

text
请求进入
  │
  ▼
关卡 A:先执行前半段
  │
  ▼
关卡 B:先执行前半段
  │
  ▼
关卡 C:先执行前半段
  │
  ▼
业务核心舱
  │
  ▼
关卡 C:再执行后半段
  │
  ▼
关卡 B:再执行后半段
  │
  ▼
关卡 A:再执行后半段
  │
  ▼
响应离开

这就是为什么 Koa 的中间件写法几乎总长这样:

js
app.use(async (ctx, next) => {
  console.log('关卡 A 进入')
  await next()
  console.log('关卡 A 返回')
})

app.use(async (ctx, next) => {
  console.log('关卡 B 进入')
  await next()
  console.log('关卡 B 返回')
})

app.use(async (ctx) => {
  console.log('核心舱处理')
  ctx.body = { ok: true }
})

实际顺序是:

text
关卡 A 进入
关卡 B 进入
核心舱处理
关卡 B 返回
关卡 A 返回

这和很多人脑中的“一个中间件跑完,再到下一个”不一样。
Koa 不是排队过栅栏,它更像:

先一层层往里钻,再一层层往外收。

这件事的工程价值非常大:

  • 统计耗时很自然
  • 统一日志很自然
  • 统一错误处理很自然
  • 统一响应包装很自然
  • 对下游结果再加工也很自然

比如最经典的响应时间中间件:

js
app.use(async (ctx, next) => {
  const start = Date.now()
  await next()
  const ms = Date.now() - start
  ctx.set('X-Response-Time', `${ms}ms`)
})

这个写法之所以成立,就是因为:

text
先记开始时间
   │
   ▼
让请求继续深入
   │
   ▼
等最里面处理完返回
   │
   ▼
再补上耗时信息

如果你没把这一张图记住,你就永远只是“会写 Koa”,不会“真正理解 Koa”。