对于异常,咱们能够分为 已知异常 和 未知异常node
已知异常就是程序中可以知道的异常,如:客户端参数传递错误,服务端抛出异常、如客户端无权限访问等等这类,这类错误就是已知异常。git
未知异常就是程序中不能预想的错误,最多见的服务器程序抛出状态码 500 的异常。又好比咱们单词拼写错误,致使程序没法运行等等,这种就是未知异常。github
咱们在中间件抛出一个异常看看json
app.use((ctx, next) => {
throw Error('error');
ctx.msg += 'world';
next();
});
复制代码
咱们在控制台获得 UnhandledPromiseRejectionWarning Error:error 错误服务器
咱们如今加上 **app.onerror ** 来拦截这个错误app
app.use((ctx, next) => {
throw Error('error');
ctx.msg += 'world';
next();
});
app.use(ctx => {
ctx.body = 'hello word';
});
app.onerror = (err) => {
console.log('捕获到了!', err.message);
}
复制代码
再次运行,发现 onerror 竟然没有拦截到koa
查阅官网中记载的错误处理方法 Error Handlingasync
其实吧,咱们违反了 koa 的设计,有两种方法处理这个问题。函数
若是不想改为 async 函数,那就在全部 next() 前面加上 return 便可。测试
若是是 async 函数,那全部 next 前面加 await 便可
知道了这个咱们开始编写全局异常中间件
编写捕捉异常处理中间件 catchError.js
const catchError = async (ctx, next) => {
try {
await next()
} catch (error) {
}
}
module.exports = catchError
复制代码
在 app.js 加载中间件
全局异常中间件监听、处理,所以放在全部中间件的最前面
const catchError = require('./middlewares/exception')
const app = new Koa()
app.use(catchError)
...
app.listen(8000)
复制代码
咱们来测试下可否正确拦截到异常
const Koa = require('koa');
const app = new Koa();
const catchError = async (ctx, next) => {
try {
await next()
} catch (error) {
console.log(error);
}
}
app.use(catchError)
app.use((ctx, next) => {
console.log(a);
});
app.listen(8000);
复制代码
咱们运行发现 控制台成功拦截错误 ReferenceError: a is not defined
向上面的 a 是空值 而咱们使用了它,致使空指针异常,这类确定是属于咱们前面说的未知异常。那咱们怎么区分是已知仍是未知呢?
在服务器接口开发中,一个异常的返回结果,一般包含有:
所以咱们能够定义 HttpException 只要是出现这异常属于HttpException都属于已知异常。
// 定义HttpException继承Error这个类
class HttpException extends Error {
constructor(msg = '服务器异常', errorCode = 500, code = 400) {
super()
/** * 错误信息 */
this.msg = msg
/** * Http 状态码 */
this.code = code
/** * 自定义的异常状态码 */
this.errorCode = errorCode
}
}
复制代码
咱们如今改写下中间件那里,区分是已知异常仍是未知异常
const { HttpException } = require("../core/httpException")
const catchError = async (ctx, next) => {
try {
await next()
} catch (error) {
const isHttpException = error instanceof HttpException
//判断是不是已知错误
if (isHttpException) {
ctx.body = {
msg: error.msg,
errorCode: error.errorCode,
request: `${ctx.method} ${ctx.path}`
}
ctx.status = error.code
} else {
ctx.body = {
msg: '服务器出现了未知异常',
errorCode: 999,
request: `${ctx.method} ${ctx.path}`
}
ctx.status = 500
}
}
}
复制代码
Ok,咱们如今在 app.js 测试下
咱们启动服务 访问 http://localhost:8000/ 发现抛出了 {"msg":"服务器出现了未知异常","errorCode":999,"request":"GET /"}
说明咱们自定义的已知错误被拦截到了.
有了上面的 异常的基类,咱们很容易定义一些常见的异常,如:参数校验失败
class ParameterExceptio extends HttpException {
constructor(msg, errorCode) {
super()
this.code = 400;
this.msg = msg || '参数错误';
this.errorCode = errorCode || 400;
}
}
复制代码
成功返回
class Success extends HttpException {
constructor(msg, errorCode) {
super()
this.code = 200;
this.msg = msg || '成功';
this.errorCode = errorCode || 200;
}
}
复制代码
权限认证
class AuthFaild extends HttpException {
constructor() {
super()
this.code = 401;
this.msg = '认证失败';
this.errorCode = 1004;
}
}
复制代码
你们能够根据本身业务定义你们须要的异常状况
前面咱们在全局异常中间件那里区分了 已知异常异常 和 未知异常。可是返回了同样的错误,这对咱们开发环境 调试是不友好的。咱们须要在控制台抛出异常,方便咱们定位问题
改写 pack.json 启动命名
"scripts": {
"start": "set NODE_ENV=dev&& nodemon --inspect app.js"
}
复制代码
咱们启动服务后告诉当前是开发环境,前面已经说了,咱们只须要在开发环境中抛出未知错误,以下:
const isHttpException = error instanceof HttpException
// 开发环境下输出 异常 error
if (process.env.NODE_ENV === 'dev' && !isHttpException) {
throw error
}
...
复制代码
通过测试,咱们能够抛出那些未知错误,到此整个中间件编写完成!