全栈项目|小书架|服务器开发-Koa2 全局异常处理

什么是异常

作开发的基本都知道异常,像Android开发中常见的ANR异常、空指针异常,服务器开发中常常遇到的异常404,500异常,还有一些其余常见的异常,具体可见HTTP状态码数据库

基本上这些异常能够总结为:已知异常未知异常api

已知异常就是程序中可以预想到异常,好比:服务器接口开发中某个api接口须要5个参数,而用户传递的参数多余5个或者少于5个,这种错误就是已知错误。服务器

未知异常就说程序中不能预想到的异常,好比:服务器接口开发中遇到了空指针而程序中又没有作相应处理就会抛出HTTP状态码为500的这种异常,这种就说未知异常。微信

为何须要全局异常处理

当遇到异常时若是没有全局异常处理,通常是在相应的代码逻辑中添加异常捕捉(try ... catch)或者抛出(throw)处理。网络

这么作实际上是有弊端的:app

  1. 程序代码判断逻辑过长,可读性查,不方便后期维护。
  2. 代码耦合性高,每次出现异常都须要在不一样的类、文件下写异常判断逻辑。

以上只是列举的几个弊端,为了解决以上的问题程序中添加全局异常的处理就颇有必要了。async

这里使用的是NodeJS+Koa2开发。在Koa中,中间件是无处不在,因此这里全局异常的处理也是经过中间件的方式去实现。工具

如何处理

一、明确是否须要抛出异常开发工具

在服务器接口开发中须要明确是生产环境仍是开发环境ui

生产环境中若是出现异常须要将详细的异常信息上报同时将异常状态经过api返回给客户端处理

开发环境中若是出现异常则须要将详细的异常信息在开发工具的控制台显示,同时返回将异常状态经过api返回给客户端处理。

这里的区别就说生产环境开发环境,因此经过定义一个全局变量去判断便可。因为程序中全局变量可能不止一个,为了统一声明全局变量,咱们将全部的全局变量放在一个文件中,统一去加载。

新建一个config.js,里面的environment:'dev'就是对环境声明的变量,这里dev表示开发环境prod表示生产环境

module.exports = {
    // prod 表示生产环境
    environment:'dev',
    database:{
        dbName:'book',
        host:'localhost',
        port:3306,
        user:'用户名',
        password:'密码',
    },
    // token 设置为1天
    security:{
        secretKey:"密钥,要记住不能弄丢了哦",
        expiresIn:60*60*24
    },
    host:'http://localhost:3000/'
}

配置了全局变量以后,在init.js文件的InitManager类中定义静态方法:

/**
 * 加载全局配置文件
 * @param {''} path 当前路径
 */
static loadConfig(path = '') {
    const configPath = path || process.cwd() + '/config/config.js'
    const config = require(configPath)
    global.config = config
}
/**
 * 加载全局异常
 */
static loadHttpException(){
    const errors = require('./http-exception')
    global.errs = errors
}

最后在app.js中完成初始化。

const app = new Koa()
InitManager.loadConfig()
InitManager.loadHttpException()

定义全局变量以后就须要制定返回的api异常描述

二、定义异常的返回结果

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

  • msg:异常描述
  • errorcode:自定义的异常状态码
  • code:网络请求的状态码

两个code的区别:

  • errorcode :自定义的错误码,配合code定位具体的异常。
  • code:网络请求的状态码,如403 权限受限,而权限受限的缘由有不少种,好比未登陆或者登陆了可是权限不足,这时候能够结合自定义的错误码和异常描述准确明确告知用户出错的缘由。

定义异常描述以后就须要去判断程序是已知异常仍是未知异常。

三、明确是已知异常仍是未知异常

已知异常:能够定义HttpException继承Error这个类,只要是出现这异常属于HttpException都属于已知异常。

http-exception.js

/**
 * 默认的异常
 */
class HttpException extends Error{
    constructor(msg='服务器异常',errorCode=10000, code=400){
        super()
        this.errorCode = errorCode
        this.code = code
        this.msg = msg
    }
}

/**
 * 资源未找到提示
 */
class NotFound extends HttpException{
    constructor(msg, errorCode) {
        super()
        this.msg = msg || '资源未找到'
        this.errorCode = errorCode || 10000
        this.code = 404
    }
}

module.exports = {
    HttpException,
    NotFound
}

环境变量声明了、异常也作了处理,那么如何监听全局异常呢?

四、全局异常监听

首先编写捕捉异常处理中间件catchError.js

const {HttpException} = require('../core/http-exception')

const catchError = async (ctx, next)=>{
    try {
        await next()
    } catch (error) {
        // 已知异常
        const isHttpException = error instanceof HttpException
        // 开发环境
        const isDev = global.config.environment === 'dev'
         // 在控制台显示未知异常信息:开发环境 不是HttpException 抛出异常
        if(isDev && !isHttpException){
            throw error
        }
        
        /**
         * 是已知错误,仍是未知错误
         * 返回:
         *      msg 错误信息
         *      error_code 错误码
         *      request 请求的接口路径
         */
        if(isHttpException){
            ctx.body = {
                msg:error.msg,
                error_code:error.errorCode,
                request:`${ctx.method} ${ctx.path}`
            }
            ctx.status = error.code
        }
        else{
            ctx.body = {
                msg: '服务器出现了未知异常',
                error_code: 999,
                request:`${ctx.method} ${ctx.path}`
            }
            ctx.status = 500
        }
    }
}

module.exports = catchError

而后在app.js中加载中间件

const catchError = require('./middlewares/exception')
const app = new Koa()
// 全局异常中间件监听、处理,放在全部中间件的最前面
app.use(catchError)
app.listen(3000)

以上就完成了全局异常的处理,接下来就是如何使用这个全局异常了。

五、出现异常后及时抛出异常

这里以资源未找到为例。在查询数据库中,有的时候会出现数据找不到状况,这是用就能够抛出资源未找到的异常。

models/book.js

/**
  * 获取书籍详情
  * @param {书籍id} bid 
  */
 static async detail(bkid) {
     const book =await Book.findOne({
         where: {
             bkid
         }
     })
     if (!book) {
        // 经过全局异常的方式,抛出资源未找到的错误
         throw new global.errs.NotFound()
     }
     return book
 }

咨询请加微信:轻撩便可。
在这里插入图片描述

相关文章
相关标签/搜索