前言

程序运行过程中常常会出现各种问题,例如业务流程分析的疏漏,边界设计不充分,调用接口时出现的错误,或者返回值与预期不符。

在设计,开发 ,测试,生成环境中得快速精准的定位到问题并制定出合理解决方案尤为重要。

如何规范化异常,避免异常到处被吞或到处都是异常捕获代码,

通用化异常处理,降低避免coding过程中对异常的关注度对开发人员来说非常重要

设计思路

在服务端收到客户端整个请求完整链路中进行异常捕获。express自带异常处理使得操作起来更为便捷!

app.use(function (err, req, res, next) {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

但是由于某些情况下,express无法捕获异常,当然很幸运是已经有人

已经写好了轮子ExpressJS Async Errors

使用非常简单,原理是通过重写express 的Layer和Router 实现

const express = require('express');
require('express-async-errors');
const User = require('./models/user');
const app = express();

app.get('/users', async (req, res) => {
  const users = await User.findAll();
  res.send(users);
});

编码

设计全局异常handler 使其在express 可以被注册

const BizResult = require('../api/BizResult');
const BizException = require("./BizException");

/**
 * @author ycx
 * @description 统一异常拦截
 */
class GlobalExceptionHandler {
    /**
     *
     * @param app
     */
    static handler(app) {
        app.use((err, req, resp, next) => {
            // set locals, only providing error in development
            resp.locals.message = err.message || err.msg;
            resp.locals.error = req.app.get('env') === 'development' ? err : {};

            //业务异常
            if (err instanceof BizException) {
                //响应业务异常数据
                resp.send(BizResult.bizFail(err));
                //其他异常
            } else {
                //记录异常日志
               //给前端响应异常信息 返回统一的响应数据
                resp.send(BizResult.fail(err.message))
            }

            resp.end();
        })
    }
}

module.exports = GlobalExceptionHandler

在项目启动链路中使用让其注册在express web 项目框架中,例如在app.js中:

const express = require('express');
const app = express();
//异常适配器
exceptionHandler.handler(app);

当然,这样设计异常并不是很合理的,在使用过程中不仅仅有业务异常对象,还有例如SQL异常对象,或者其他的自定义Error 对象,那么以下代码过长是及其不利于维护,

      //业务异常
            if (err instanceof BizException) {
                //响应业务异常数据
                resp.send(BizResult.bizFail(err));
                //其他异常
            } else {
                //记录异常日志
               //给前端响应异常信息 返回统一的响应数据
                resp.send(BizResult.fail(err.message))
            }

所以,使用必要的设计模式是非常可取的,例如使用策略模式去优化它

写一个map方法去装下对象,例如:

/**
* 上下文
**/
class ExceptionContent{
	 const errHandlerMap = new Map();
	 map.put('BizException',new BizExceptioHandler());
}

/**
* 业务异常
**/
class BizExceptioHandler{
    
    /**
    * 适配这个异常的方法
    **/
    static handler(resp,err){
        //响应业务异常数据
        resp.send(BizResult.bizFail(err));
    }
}
// 其他异常 和业务异常适配code一致,但要注意都必须遵守写的方法内包含 相同方法 例如 handler 方法
....

在全局异常处理中去使用它

/**
 * @author ycx
 * @description 统一异常拦截
 */
class GlobalExceptionHandler {
    /**
     *
     * @param app
     */
    static handler(app) {
        app.use((err, req, resp, next) => {
            // set locals, only providing error in development
            resp.locals.message = err.message || err.msg;
            resp.locals.error = req.app.get('env') === 'development' ? err : {};

           let expHandler= ExceptionContent.errHandlerMap(err.name);
           
            // 未处理到的异常
           if(null === expHandler){
                resp.send(BizResult.fail(err.message))
           }
            //处理异常
          	expHandler.handler(resp,err);
            resp.end();
        })
    }
}

module.exports = GlobalExceptionHandler

由于js 无法强制化规范化写的代码 ,例如java中的抽象类,所以实现一些代码,需要开发人员必要的耐心和规范化设计

结语

合理的处理异常,可以给client端更好的处理一些异常,即是做好交互,避免失去响应等,也让server端开发人员不需要注重异常处理,提高开发生产效率。精准快速定位异常,并修复它。