1.前言
这节我们主要讲 Koa 常见错误处理方式,总的来讲,Koa 处理异常还是比较方便简单的。
2.简单使用
2.1 ctx.throw(status, message)
Koa 在 content 上下文挂载了一个 throw 对象,专门来处理错误,使用也比较简单。
// controller/user.js
function detail(ctx) {
const id = ctx.params.id;
const {name} = ctx.query;
if(!name) {
ctx.throw(500, 'name is required');
}
ctx.body = {
data: array[id],
success: true
};
}
然后我们访问 http://localhost:3000/user/detail/1, 就会返回下面错误:
可以看到状态码返回的是 500,但是message 是 Internal Server Error, 并不是我们的 name is required,这是为什么,这是Koa 的一种安全措施,因为 500 代表这后端异常,比如可能是数据库异常,这种错误抛出去是非常不安全的,但是你换成其他 状态码就不会这样,比如换成 422,浏览器显示如下:
有的人喜欢 直接 throw 异常,比如:
// controller/user.js
function detail(ctx) {
const id = ctx.params.id;
const {name} = ctx.query;
if(!name) {
throw new Error('name is required');
}
ctx.body = {
data: array[id],
success: true
};
}
这这种方式和 和 ctx.throw(500, message) 表现一样,都是出现 Internal Server Error
2.2 中间件统一处理 error
下面是官网例子,注意哈,因为 Koa 中间件是洋葱模型,所以应该把处理错误中间件放在最前面,第一个加载,这样才能捕捉到所有错误。
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
if(ctx.status === 500) {
ctx.body = '服务器异常,请联系管理员';
}
ctx.app.emit('error', err, ctx);
}
});
app.on('error', (err, ctx) => {
// 这里可以做个通知处理,比如发个邮件,钉钉消息啥的
});
Koa官方的错误介绍到这,是不是很简单,一下子就会,太简单了,等等,图样图森破,下面讲讲坑。
3. I can’t catch the error
先来段代码:
const Koa = require('koa')
const app = new Koa()
const router = require('./router')
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
if(ctx.status === 500) {
ctx.body = '服务器异常,请联系管理员';
}
ctx.app.emit('error', err, ctx);
}
});
app.use(async (ctx, next) => {
throw Error('这里泡个错');
ctx.msg += 'world';
next();
});
app.on('error', (err, ctx) => {
console.log('error handle:', err)
// 这里可以做个通知处理,比如发个邮件,钉钉消息啥的
});
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000, () => {
console.log(' starting at port 3000')
})
访问下http://localhost:3000/user/detail/1,控制台会打印如下错误
这里很好的可以看到能看到错误,接下来加几行代码,如下:
const Koa = require('koa')
const app = new Koa()
const router = require('./router')
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
if(ctx.status === 500) {
ctx.body = '服务器异常,请联系管理员';
}
ctx.app.emit('error', err, ctx);
}
});
//新增代码
app.use(async (ctx, next) => {
ctx.msg = 'hello';
next();
});
//新增代码
app.use(async (ctx, next) => {
ctx.msg += ' ';
next();
});
app.use(async (ctx, next) => {
throw Error('这里泡个错');
ctx.msg += 'world';
next();
});
app.on('error', (err, ctx) => {
console.log('error handle:', err)
// 这里可以做个通知处理,比如发个邮件,钉钉消息啥的
});
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000, () => {
console.log(' starting at port 3000')
})
访问下http://localhost:3000/user/detail/1,控制台会打印如下错误
这里出现了 UnhandledPromiseRejectionWarning: Error, 我们通过控制台无法定位到 异常,看不到错误具体行号,
我们把上面代码再改一下
一个中间件在 next 加上 await,一个加上 return, 这个时候我们再次访问,发现又能看到错误具体行号了,这就是 Koa Github 上 著名 I can’t catch the error 。
所以中间件 使用过程中记得,next 记得 用 await 或者 return,这样 promise 的链条才不会断掉。
4. 总结
总的来说,Koa 处理 异常还是非常方便的,同时记得在中间件 记得 await next() 或者 return next(),这样才不会出现某些异常捕获不到。最后这节 Demo 地址,可以对着 Demo 走一遍