Koa 的错误处理,和它的洋葱模型是强绑定的。
最稳的姿势通常是:
- 最外层用一个
try/catch中间件兜底 - 业务里用
ctx.throw()/ctx.assert() - 再配合
app.on('error')统一记日志
这是 Koa 项目最常见、也最成熟的写法。
import Koa from 'koa'
const app = new Koa()
app.use(async (ctx, next) => {
try {
await next()
} catch (err) {
ctx.status = err.status || 500
ctx.body = {
message: err.expose ? err.message : '巡防塔内部故障',
}
ctx.app.emit('error', err, ctx)
}
})
app.use(async (ctx) => {
ctx.throw(403, '你没有进入核心舱的权限')
})
app.on('error', (err, ctx) => {
console.error('server error', {
message: err.message,
path: ctx.path,
method: ctx.method,
})
})
app.listen(3000)这套机制的心智非常清楚:
内层关卡发现事故
│
▼
throw err / ctx.throw(...)
│
▼
最外层总控室 catch 住
│
├── 给客户端一个统一回执
└── 发 error 事件做日志记录Koa 官方默认也有错误处理逻辑:
- 没开启
app.silent时会往 stderr 打错误 - 还能在能响应时自动给 500
- 同时会发出 app 级别
error事件
但工程里你几乎都会自己接一层错误中间件,因为你需要:
- 统一 JSON 结构
- 统一错误码
- 统一日志格式
- 统一 trace id
- 控制哪些错误信息能暴露给客户端
一句话记住:
业务关卡负责发现事故,最外层负责回收事故。