错误处理
系统在处理用户请求时,会遇到各种各样的错误情况。如:系统内部错误,url 不存在,没有权限,服务不可用等,这些情况下都需要进行错误输出。
控制器错误处理
在控制器中,错误的输出分为三种情况:
- API接口错误输出,格式为JSON
this.fail(errmsg, data, code, options);
or
this.error(errmsg, data, code, options);
errmsg: 错误提示信息
data: 输出数据项
code: 错误码,未传入默认500
options:定义错误输出的JSON对象key值
- 给用户显示错误页面
//传入参数为错误模板物理路径
this.assign('errmsg', '没有权限');
this.render(process.env.APP_PATH + '/view/default/error.html');
- 抛出http错误状态
//抛出404错误
this.ctx.throw(404, '未找到页面');
使用此方式抛出错误,框架会自动拦截,并显示默认的404或500等错误页面。对于用户来讲,并不是很好的体验,建议使用前两种方式做错误处理。
中间件错误处理
中间件中的错误处理直接操作ctx上下文即可:
//使用此方式抛出错误,框架会自动拦截,并显示默认的404或500等错误页面
ctx.throw(500, '发生错误');
return; //直接返回,不再执行下一个中间件
推荐的处理方式
:
//使用自定义的错误页面
ctx.body = await ctx.compile(templateFile, data);
return;
//或者返回JSON
ctx.body = {"err_code": 500, "err_msg": "发生错误"};
return
服务类或其他文件内处理错误
服务类或其他文件,可以使用Promise.reject()或者 throw Error的方式来抛出错误:
return Promise.reject('错误');
//或者
throw Error('发生错误');
需要注意的是,服务类等功能性的模块,并不会直接提供给http访问,http访问只会经过中间件和控制器。中间件或控制器在调用服务类或其他外部模块的时候,需要对这些外部模块抛出的错误进行拦截和处理,如果没有拦截处理,ThinkKoa框架会自动拦截,并返回统一的http 500 错误页。
PREVENT_NEXT_PROCESS错误
我们在开发的时候,如果使用promise的写法,例如:
let testService = new test();
return testService.getUserInfo().then(info => {
return this.ok('获取用户信息成功', info);
}).catch(e => {
return this.fail('获取用户信息失败' + e.message);
});
往往会出现一个奇怪的错误:
Error: PREVENT_NEXT_PROCESS
...
这是因为在调用控制器 this.ok、this.fail、this.json、this.jsonp、this.render、this.display等方法时,框架在输出response之后,会自动抛出一个Promise.reject("PREVENT_NEXT_PROCESS")来中断后续代码执行。这个reject异常被上述代码的catch拦截后,就会出现这个错误。
处理方法:
1、使用 async/await:
let testService = new test();
let info = await testService.getUserInfo().catch(e => {
return this.fail('获取用户信息失败' + e.message);
});
return this.ok('获取用户信息成功', info);
2、调换 then和 catch的顺序:
let testService = new test();
return testService.getUserInfo().catch(e => {
return this.fail('获取用户信息失败' + e.message);
}).then(info => {
return this.ok('获取用户信息成功', info);
});