Express.js 服务器全局错误处理中间件

715 阅读4分钟

Express.js 服务器全局错误处理中间件

简介

Web 服务器上的有效错误处理是开发弹性系统的关键组成部分。通过提供有用的信息和准确的错误代码,您将能够快速排除故障。

确保 Express 捕获运行路由处理程序和中间件时发生的所有错误非常重要。路由处理程序和中间件内的同步代码中发生的错误不需要额外的工作。

对于由路由处理程序和中间件调用的异步函数返回的错误,您必须将它们传递给函数next()

本文将构建一个完整的例子;

快速创建服务器

创建项目

mkdir express-error-handling
cd express-error-handling
npm init
npm i express nodemon
  • 创建项目目录并移动到目录下
  • 初始化项目
  • 安装所需依赖

运行服务器

(app.js)
const express = require('express')
const app = express()
const port = 3000
​
app.get('/', (req, res) => {
  res.send('Welcome to error handling in Express :)');
});
​
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
  • 创建一个app.js文件
  • 在app.js创建服务器实例在端口 3000 上侦听传入的 HTTP 请求。
  • GET定义处理 HTTP请求 的根路由
  • 在终端执行nodemon app启动项目

构建组件以有效处理错误

错误处理程序中间件: 在请求和响应之间运行的任何程序都称为中间件。此场景中的错误处理程序中间件是将在服务器中处理的许多错误的抽象。

(./middleware/handleError.js)
const AppError = require('../utils/AppError');
​
const handleError = (err, req, res ,next) => {
    if (err instanceof AppError) {
        return res.status(err.statusCode).json({
            isSuccess:false,
            message: err.message,
            status: err.status
        });
    }
​
    res.status(500).json({
        isSuccess:false,
        message: 'Internal Server Error',
        status: 500
    });
}
​
module.exports = handleError;
  • 错误处理程序中间件接受四个参数:error,request,response,next(注意:这四个参数都要有,否则就不是错误处理中间件而是普通中间件)
  • 如果传递的错误是自定义 AppError 的实例,handleError则函数会使用 statusCode、errorMessage 和 errorStatus 进行响应。
  • 如果错误不是 AppError 的实例,则响应将是 500 errorCode 和内部服务器错误消息。
(./utils/AppError.js)
class AppError extends Error {
    constructor(statusCode,message) {
        super(message);
        this.statusCode = statusCode;
        this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
    }
}
​
module.exports = AppError;

AppError 类: 名为 AppError 的自定义Error类继承了new Error(..)基本Error类。

  • 它接受message参数和errorCode.
  • Super 方法使用message参数传递给父类Error构造函数。
exports.tryCatch = (controller) => {
    return async (req, res, next) => {
        try {
            let result = await controller(req, res);
            return result;
        } catch (err) {
            console.log("err",err);
            next(err);
        }
    }
}

tryCatch抽象函数:在接收到controller作为参数时提供带有 try-and-catch 块的中间件。

  • controller在try块中调用,如果发生错误,则在catch块中 调用next并将error作为参数传入。

加载中间件

const express = require('express')
const handleError = require('./middleware/handleError')
const router = require('./router')
const app = express()
const port = 3000
​
app.get('/', (req, res) => {
    res.send('Welcome to error handling in Express :)');
});
​
//路由中间件
app.use(router)
​
//错误处理中间件
app.use(handleError)
​
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
  • handleError中间件将用于处理 HTTP 过程中发生的错误。
  • router中间件将用于处理对应http操作的业务逻辑

创建Express Router示例

  • 创建一个返回用户操作的用户Router。该Router将用于试验几个可在 HTTP 进程中处理的操作错误。
(router.js)
const express = require('express');
const router = express.Router();
const { getUsers } = require('./service/user');
const { tryCatch } = require('./utils/tryCatch');
​
const userController = tryCatch(async (req, res) => {
    const users = await getUsers(req.body);
    res.status(200).json({
        isSuccess: true,
        data: users
    })
});
​
router.get('/user', userController);module.exports = router;

web服务器常见错误示例

Bad Request Error,通常称为 400: 当 Web 服务器无法处理 Web 客户端给出的请求时,400 错误是出现的 HTTP 响应状态的一种形式。这种错误很可能是由网络客户端引起的。有几种情况可能会导致 400 错误。

  • 当网络 URL 包含错误时
  • 当 Web 客户端向 Web 服务器提供不准确或不完整的信息时。
  • 用户的浏览器或互联网连接问题

代码示例

我们可以处理无效url问题,以下是代码示例:

(app.js)
app.get('*', (req, res) => {
    throw new AppError(400, 'Invalid endpoint');
});

当所有路由都匹配不上时,会走通配符*的路由,代表url无效。

Express Router 中的星号字符可用于匹配路由中任意数量的字符。例如,当指定的 URL 与任何现有路由都不匹配时,将触发星号路由。在这种情况下,Web 服务器返回 400 错误代码。

未经授权的错误,通常称为 401: 当 Web 客户端在没有必要权限的情况下与 Web 服务器通信时,Web 服务器以 401 错误响应,表明用户无权执行预期的动作。

代码示例

为了演示 401 错误,让我们构建一个用户身份验证中间件,它在完成用户请求之前检查用户是否已获得授权。

(./middleware/authenticated.js)
const AppError = require('../utils/AppError');
​
const authenticated =  (req, res, next) => {
  const isAuthenticated = req.headers.authorization;
​
  if (!isAuthenticated) {
    throw new AppError(401, 'unauthorized request');
  }
  next()
}
​
module.exports = authenticated;

然后将在用户路由中使用授权的中间件。

(./route.js)
​
router.get('/user', authenticated, userController);

未找到错误,通常称为 404: 当 Web 客户端可以与 Web 服务器交互但 Web 服务器无法定位所请求的资源时,会出现 404 HTTP 响应状态。

代码示例

假设此时服务器上没有user。当我们请求user时,服务器返回 404 错误。可以在contoller上进行修改。

(./service/user.js)
const AppError = require('../utils/AppError');
module.exports.getUsers = async function (body) {
    let user = undefined;
    if (user) {
        return {user:user}
    } else {
        throw new AppError(404,"not found user")
    }  
}

内部服务器错误,通常称为 500: 500 响应状态表示 Web 服务器问题,与 Web 客户端无关。以下因素可能会导致 500 错误的发生:

  • 当 Web 服务器供应商出现问题时
  • 数据库连接失败的原因
  • Web 服务器代码中出现编程错误。

结论

在 Web 服务器上始终具有正确的错误处理结构至关重要,以便最大限度地排除在 Web 服务器上工作时可能发生的特定操作错误的可能性。