Express实现WebAPI之错误异常处理

概要

Express 是一个简洁而灵活的 node.js Web应用框架, 提供一系列强大特性帮助你创建各种Web应用。本文主要介绍如何使用express框架的中间件来实现WebAPI的异常,错误捕获处理。

设计和实现

项目背景知识和程序中已有代码,请参考我的博文Express实现WebAPI之请求实现与调试

错误处理相关类的设计思想是所有API的error类都继承自JS的Error基类,每种错误类型对应一个Http状态码(400到599)本文只列举出了常用的几个错误类型,类图如下所示:

Express实现WebAPI之错误异常处理_第1张图片

  1. CustomerAPIError对应500,服务内部错误,message字段可以修改。
  2. BadRequestError对应400,bad request错误,message字段可以修改。
  3. UnauthError对应401,无权限错误,即权鉴失败对应的错误,message字段可以修改。

CustomerAPIError代码实现

const {StatusCodes, ReasonPhrases} = require("http-status-codes");
class CustomerAPIError extends Error{
    constructor(message){
        if (!message){
            message = ReasonPhrases.INTERNAL_SERVER_ERROR;
        }
        super(message);
        this.statusCode = StatusCodes.INTERNAL_SERVER_ERROR;
    }
}

module.exports = CustomerAPIError;
  1. message 默认是http-status-codes库中对应的值。
  2. 如果用户指定了message 字段,则默认值被覆盖。
  3. statusCode 按照http-status-codes库对应的规范,定义为500。

BadRequestError代码实现

const CustomerAPIError = require("./CustomerApiError");
const {StatusCodes, ReasonPhrases} = require("http-status-codes");
class BadRequestError extends CustomerAPIError{
    constructor(message){
        if (!message){
            message = ReasonPhrases.BAD_REQUEST;
        }
        super(message);
        this.statusCode = StatusCodes.BAD_REQUEST;
    }
}
module.exports = BadRequestError;

  1. message 默认是http-status-codes库中对应的值。
  2. 如果用户指定了message 字段,则默认值被覆盖。
  3. statusCode 按照http-status-codes库对应的规范,定义为400。

UnauthError代码实现

const CustomerAPIError = require("./CustomerApiError");
const {StatusCodes, ReasonPhrases} = require("http-status-codes");
class UnAuthError extends CustomerAPIError{
    constructor(message){
        if (!message){
            message = ReasonPhrases.UNAUTHORIZED;
        }
        super(message);
        this.statusCode = StatusCodes.UNAUTHORIZED;
    }
}
module.exports = UnAuthError;
  1. message 默认是http-status-codes库中对应的值。
  2. 如果用户指定了message 字段,则默认值被覆盖。
  3. statusCode 按照http-status-codes库对应的规范,定义为401。

出口文件

上述每个错误处理类,对应一个JS文件,都放在项目的error目录下,并增加一个index.js出口文件,导出所有的错误处理类,方便其它代码解构使用,代码如下:

const CustomerAPIError = require("./CustomerApiError");
const BadRequestError  = require("./BadRequestError");
const UnAuthError = require("./UnauthError");
module.exports = {
    CustomerAPIError, BadRequestError, UnAuthError
};

错误处理中间件

定义exceptionHandler中间件,用于捕获代码中抛出的异常错误,代码如下:

const {CustomerAPIError} = require("../errors");

const exceptionHandler = async (err,req,res, next) => {
    const customerError = new CustomerAPIError();
    if (err instanceof CustomerAPIError){
        return res.status(err.statusCode).json({
            msg: err.message
        });
    }
    return res.status(customerError.statusCode).json({
        msg: customerError.message,
        err
    });
}
module.exports = exceptionHandler;
  1. 中间件函数第一个参数就是捕获的错误处理对象;
  2. 如果错误处理对象是CustomerAPIError 或其派生类定义,则返回对应的错误码和错误信息;
  3. 如果是未知错误类型,反回500 服务器内部错误和错误信息;
  4. 该中间件文件放到middleware目录中,同样通过index.js文件导出,便于解构使用。代码如下:
const exceptionHandler = require("./exceptionHandler");
module.exports = {
    exceptionHandler
}

服务器实例引用该中间件

require('dotenv').config({path:'.env'});
const express = require('express');
const app = express();
require("express-async-errors");
const bodyParser = require("body-parser");
const port = process.env.PORT || 5000;
const msgRouter = require("./routes/test/");
const {exceptionHandler} = require('./middlewares')


app.use(bodyParser.urlencoded({
    extended: true
}));
app.use(bodyParser.json());

app.use('/api/v1', msgRouter);
app.use('/api/v1', exceptionHandler);
const start = async () => {
    try {
        app.listen(port, ()=>{
            console.log(`Server is listening on port ${port}...`)
        });
    } catch (error) {
        console.log(error);
    }
}

start();
  1. 增加require(“express-async-errors”);的引用,它可以帮助我们在中间件中捕获async函数抛出的自定义异常或错误。
  2. const {exceptionHandler} = require(‘./middlewares’), 以解构方式导入错误处理的中间件。
  3. app.use(‘/api/v1’, exceptionHandler); 在/api/v1下面的所有WebAPI都将使用exceptionHandler错误处理中间件。

代码验证

我们在下面Post方法中,增加对表单数据age栏位的验证,该栏位必须是数字,否则抛出异常。代码如下:

const postMsg =  async (req,res) => {
    const {age,name} = req.body;
    const {id} = req.params;
    if (isNaN(new Number(age))){
        throw new BadRequestError("Age should be number");
    }
    res.status(StatusCodes.OK).json({
        msg : `post request is success `  ,
        forms: `form parameter age is ${age} and name is ${name}`,
        id:id
    });
};
  1. Action方法全部代码请参考我的博文Express实现WebAPI之请求实现与调试
  2. 如果age字段不是数字,则抛出Bad Request异常,message是Age should be number

代码调试

我们使用Thunder Client进行WebAPI的调试

Express实现WebAPI之错误异常处理_第2张图片
从执行结果可以看出,在表单中,我们的age设定为12p,它并不是一个数字,返回结果符合预期,http状态码是400,错误信息是“Age should be number”

解耦错误处理中间件

我们定义一个新的未知错误中间件,对于所有的未知的错误,都交给它来处理。让exceptionHandler只处理已知的异常错误。这样便于处理更复杂的场景,以及错误分析,定义有问题的代码。

解耦后的exceptionHandler代码如下:

const {CustomerAPIError} = require("../errors");

const exceptionHandler = async (err,req,res, next) => {
    if (err instanceof CustomerAPIError){
        return res.status(err.statusCode).json({
            msg: err.message
        });
    }else{
        next(err);
    }
}
module.exports = exceptionHandler;

未知错误处理代码如下:

const {CustomerAPIError} = require("../errors");

const unkonwnErrorHandler = async (err,req,res, next) => {
    if (!!err){
        const customerError = new CustomerAPIError();
        return res.status(customerError.statusCode).json({
            msg: customerError.message,
        });
    }
    
}
module.exports = unkonwnErrorHandler;

两个中间件应用顺序如下:

app.use('/api/v1', exceptionHandler);
app.use('/api/v1', unkonwnErrorHandler);
  1. 通过next方法连接两个中间件。
  2. 如果是未知错误,exceptionHandler中,直接通过next方法,去掉用unkonwnErrorHandler 来解决该错误。

相关文章

Express实现WebAPI之环境搭建
Express实现WebAPI之请求实现与调试
Express实现WebAPI之错误异常处理

你可能感兴趣的:(nodejs,JavaScript,javascript,node.js,中间件,restful)