“c.Next() 之后的代码”到底什么时候执行?中间件不调 Next 会怎样?响应写出以后还能改什么?panic 时哪些后半段会被跳过?这篇用打印顺序和真实客户端把洋葱模型的细节一次看清。
都调用 Next A前 → B前 → C前 → handler → C后 → B后 → A后
Next 是个普通的函数调用,所以后半段的顺序和前半段完全相反:A 前、B 前、C 前、handler、C 后、B 后、A 后。需要“包住”后面所有逻辑的事情(计时、日志、统一错误处理)放在 c.Next() 之后。
B 不调用 Next A前 → B前 → B后 → C前 → handler → C后 → A后
B 调用两次 Next A前 → B前 → C前 → handler → C后 → B后 → A后
B 调用 Abort(不调 Next) A前 → B前 → B后 → A后中间件不调 c.Next() 只是不亲自递归,外层的 Next 循环会接着调下一个 handler,所以后面的中间件和 handler 仍会执行,只是执行时机变成在它的“后”半段之后。要拦截请求必须 c.Abort()(或 AbortWithStatus 系列)。调两次 Next 没有额外效果。
中间件后半段:状态=201 已写字节=5 已写出=true Errors=1
/x 客户端收到:状态=201 正文="hello" X-Late="" X-Early=""
中间件后半段:状态=202 已写字节=-1 已写出=false Errors=0
/y 客户端收到:状态=500 正文="" X-Late="1" X-Early="1"后半段里读得到状态、已写字节数、Errors,但响应一旦写出,c.Header 和 c.Status 对客户端就无效了。handler 只设状态而没有写正文时,响应其实还没发出,后半段改状态仍然生效。写“统一加响应头”的中间件要放在 Next 之前。
Recovery 在最外层 A前 → B前 → handler → [Recovery 处理 panic] → [状态500]
Recovery 在 A 与 B 之间 A前 → B前 → handler → [Recovery 处理 panic] → A后 → [状态500]
B 用 defer 写收尾 A前 → handler → B的defer → [Recovery 处理 panic] → [状态500]panic 会跳过沿途所有“后”半段,直到被 Recovery 捕获。Recovery 放在最外层,所有后半段都不会执行;放在中间,只有它外面的后半段还能执行。需要在 panic 时也做收尾(释放锁、记录指标)的中间件应该用 defer。