1.前言
这节我们主要讲 Koa 常见错误处理方式,总的来讲,Koa 处理异常还是比较方便简单的。
2.简单使用
2.1 ctx.throw(status, message)
Koa 在 content 上下文挂载了一个 throw 对象,专门来处理错误,使用也比较简单。
// controller/user.jsfunction 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.jsfunction 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 走一遍
