从零构架个人博客网站(二)-全局异常处理


对于异常,我们可以分为 已知异常未知异常

已知异常就是程序中能够知道的异常,如:客户端参数传递错误,服务端抛出异常、如客户端无权限访问等等这类,这类错误就是已知异常。

未知异常就是程序中不能预想的错误,最常见的服务器程序抛出状态码 500 的异常。又比如我们单词拼写错误,导致程序无法运行等等,这种就是未知异常。

中间件的异常

我们在中间件抛出一个异常看看

app.use((ctx, next) => {
    throw Error('error');
    ctx.msg += 'world';
    next();
});

我们在控制台得到 UnhandledPromiseRejectionWarning Error:error 错误

我们现在加上 **app.onerror ** 来拦截这个错误


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 居然没有拦截到

查阅官网中记载的错误处理方法 Error Handling

其实吧,我们违反了 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 是空值 而我们使用了它,导致空指针异常,这类肯定是属于我们前面说的未知异常。那我们怎么区分是已知还是未知呢?

在服务器接口开发中,一个异常的返回结果,通常包含有:

  • msg:异常信息
  • code:Http 状态码
  • errorCode:自定义的异常状态码

因此我们可以定义 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
}
...

经过测试,我们可以抛出那些未知错误,到此整个中间件编写完成!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容

  • Java异常控制机制又被称为“违例控制机制”。捕获程序错误最理想的时机是在编译阶段,这样可以彻底避免错误的代码运行...
    kelgon阅读 4,252评论 2 50
  • 转载文章,仅用于个人查阅和学习。原文链接://www.greatytc.com/p/15872cba211...
    Jayden_Cao阅读 1,235评论 0 1
  • 今天早上听了封静的整屋整理的分享非常受益。我也会在屋子里面看到有的物品不知道什么时间买的?当时什么心情的时候买的?...
    立即行动2018阅读 70评论 0 0
  • 世界上有很多让人烦恼的事,但我最烦恼的还是我做事马马虎虎,因此,爸爸妈妈还送给我个外号叫“小马虎”。 要想知道...
    17吴陈辰阅读 643评论 0 0
  • 她是谁 一个绝美故事里的配角 一个别人世界里的过客 在姐姐鲜艳光辉的对比下 她只是一条毫不起眼的落寞的 青色的小蛇...
    Awo十里阅读 537评论 7 6