介绍
当用户与任何应用程序交互时,异常和错误必然会发生,软件工程师有意或无意地选择一种方法来处理可能出现的任何错误。 因此,使用 Express 构建 API 的后端开发人员发现他们正在努力确保他们正在构建一个有用、高效和可用的 API。 最重要的是以构建健壮系统的方式处理错误,因为这有助于减少开发时间、彻底错误、生产力问题,并决定软件开发的成功或可扩展性。
您是否需要记录错误消息、抑制错误、将错误通知用户或编写代码来处理错误? 不知道了.
在本指南中,我们将学习如何为 Express 应用程序构建健壮的错误处理代码库,这将有助于检测应用程序错误并采取最佳操作以在运行时从优雅的失败中恢复任何应用程序。
请注意: 我们将使用 Postman 来测试我们演示中的 API。 您可以在 邮递员下载页面. 或者,您可以简单地使用浏览器、命令行 curl
工具,或您可能熟悉的任何其他工具。
什么是错误处理?
在软件开发中,有两种不同的异常: 操作 和 程序化.
- 运行时可能会出现运行失败,为了防止应用程序突然终止,我们必须通过高效的错误处理方法优雅地处理这些异常。
- 当异常状态出现时,编程异常由程序员手动抛出。
您可以将操作异常视为“意外但可预见”的异常(例如越界访问索引),将编程异常视为“预期和预见”的异常(例如数字格式异常)。
异常处理是用于查找和修复程序中的缺陷的过程。 错误处理发送的消息包括发生的错误类型和发生错误的堆栈。
请注意: 在计算机科学中,异常可以从运行时期间的操作或编程问题中恢复,并且通常源于这些问题。 错误通常是由外部因素引起的,例如硬件限制、连接问题、内存不足等。在 JavaScript 中,这些术语经常互换使用,自定义异常源自 Error
班级。 这 Error
类本身代表错误和异常。
在 Express 中,异常处理是指 Express 如何设置自身以捕获和处理同步和异步异常。 Express 中异常处理的好处是,作为开发人员,您不需要编写自己的异常处理程序; Express 带有默认的异常处理程序。 异常处理程序有助于识别错误并将它们报告给用户。 它还提供各种补救策略并实施它们以减轻异常。
虽然这些看起来像是隐藏在幕后的很多东西,但 Express 中的异常处理不会减慢程序的整体进程或暂停其执行。
了解 Express 中的异常处理
使用 Express 自带的默认错误处理程序,我们手头有一组中间件函数,可以帮助自动捕获路由处理程序中的错误。 很快,我们将创建一个项目,将关于如何在 Express 应用程序中返回正确的错误以及如何不泄露敏感信息的理论付诸实践。
在 Express 中定义中间件功能
错误处理中间件函数的定义方式是它们接受一个 Error
object 作为第一个输入参数,后面是任何其他中间件函数的默认参数: request
, response
及 next
。 该 next()
函数将所有当前中间件跳过到路由器的下一个错误处理程序。
在 Express 中设置错误处理
在您的终端中运行以下命令以创建一个 Node 和 Express 应用程序:
$ mkdir error-handling-express
在新创建的文件夹中,让我们初始化一个新的 Node 项目:
$ cd error-handling-express && npm init -y
这创造了一个 package.json
文件在我们的文件夹中。
要在我们的 Node 应用程序中创建一个 Express 服务器,我们必须安装 express
包, dotenv
用于自动加载环境变量到 .env
归档到 process.env
对象,和 nodemon
如果目录中记录了文件更改,则用于重新启动节点应用程序。
$ npm install express dotenv nodemon
接下来,创建一个 app.js
项目文件夹中的文件将用作应用程序的索引文件。
现在我们已经为我们的 Express 应用程序安装了所有需要的依赖项,我们需要在 package.json
文件。 为实现这一目标, package.json
文件,以便 scripts
对象如下图所示:
"scripts": {
"start": "nodemon app.js"
},
或者,您可以跳过使用 nodemon
,并使用 node app.js
代替。
设置 Express 服务器
要设置服务器,我们必须首先将各种包导入到 app.js
. 我们还将创建一个 .env
项目目录中的文件——存储应用程序的所有环境变量:
const express = require('express')
require('dotenv').config
PORT=4000
我们已经在中定义了应用程序的端口号 .env
,它被加载并读取 dotenv
, 以后可以访问。
初始化 Express 服务器
现在,我们需要初始化 Express 服务器并让我们的应用程序监听应用程序端口号,以及对测试路由的请求 – /test
. 让我们更新 app.js
,在导入语句下面:
const app = express();
const port = process.env.PORT || 4000;
app.get("/test", async (req, res) => {
return res.status(200).json({ success: true });
});
app.listen(port, () => {
console.log(`Server is running at port ${port}`);
});
从这里开始,我们将学习如何在 Express 中处理可能遇到的操作错误的各种用例。
处理 Express 中的 Not Found 错误
假设您需要从用户数据库中获取所有用户,您可以通过将逻辑包装到 try/catch
块——希望捕获任何可能投射到 catch
块:
const getUser = () => undefined;
app.get("/get-user", async (req, res) => {
try {
const user = getUser();
if (!user) {
throw new Error('User not found');
}
} catch (error) {
console.log(error);
res.status(400).send(error.message)
}
return res.status(200).json({
success: true
});
});
结果是:
User not found
现在,当发出此请求(您可以使用 Postman 进行测试)并且数据库中不存在用户时,客户端会收到一条错误消息,指出“找不到用户”。 此外,您会注意到错误也记录在控制台中。
使用错误处理程序中间件优化错误处理
我们可以通过创建一个错误处理程序中间件来优化开发,该中间件将出现在所有已定义路由的末尾,这样如果在其中一个路由中抛出错误,Express 将自动查看下一个中间件并继续向下移动列表直到它到达错误处理程序。 错误处理程序将处理错误并向客户端发回响应。
查看我们的 Git 学习实践指南,其中包含最佳实践、行业认可的标准以及随附的备忘单。 停止谷歌搜索 Git 命令,实际上 学习 它!
首先,创建一个名为 middleware
在项目目录中,并在此文件夹中,创建一个名为 errorHandler.js
它定义了错误处理程序:
const errorHandler = (error, req, res, next) => {
console.log(error);
res.status(400).send(error.message);
}
module.exports = errorHandler;
在我们的中间件函数中,我们通过添加 error
3个基本参数之前的参数。
现在,我们将在演示中使用错误处理程序 app.js
并使用错误处理程序中间件处理获取用户的初始错误,如下所示:
const getUser = () => undefined;
app.get("/get-user", async (req, res, next) => {
try {
const user = getUser();
if (!user) {
throw new Error("User not found");
}
} catch (error) {
return next(error);
}
});
app.use(errorHandler);
我们可以进一步优化我们的代码,通过围绕 try/catch
逻辑。 我们可以通过在项目目录中创建一个名为 utils
,并在其中创建一个名为 tryCatch.js
.
抽象的 try-catch
逻辑——我们可以定义一个接受另一个函数的函数(称为 调节器) 作为其参数,并返回一个 async
将持有一个函数 try/catch
对于任何收到的控制器。
如果控制器发生错误,它会被捕获到 catch
块并调用下一个函数:
const tryCatch = (controller) => async (req, res, next) => {
try {
await controller(req, res);
} catch (error) {
return next(error);
}
};
module.exports = tryCatch;
随着 try/catch
抽象,我们可以通过跳过 try-catch
在获取用户时显式子句 app.js
:
const getUser = () => undefined;
app.get(
"/get-user",
tryCatch(async (req, res) => {
const user = getUser();
if (!user) {
throw new Error("User not found");
}
res.status(400).send(error.message);
})
);
我们已经成功地抽象出 try-catch 逻辑,我们的代码仍然像以前一样工作。
处理 Express 中的验证错误
对于这个演示,我们将在我们的 Express 应用程序中创建一个用于登录的新路由——以在登录时验证用户 ID。首先,我们将安装 joi
包,以帮助创建一个模式,我们可以用它来执行要求:
$ npm i joi
接下来,创建一个模式,它是 Joi.object
用 userId
这必须是一个数字并且是必需的——这意味着请求必须匹配一个带有用户 ID 的对象。
我们可以使用 validate()
模式对象中的方法来根据模式验证每个输入:
const schema = Joi.object({
userId: Joi.number().required(),
});
app.post(
"/login",
tryCatch(async (req, res) => {
const {error, value} = schema.validate({});
if (error) throw error;
})
);
如果传入一个空对象 validate()
方法,错误将被优雅地处理,错误信息将被发送到客户端:
在控制台上,我们还可以访问 details
包含有关错误的各种详细信息的数组,如果需要,可以将其传达给用户。
为了专门处理验证错误,以便为每个验证错误传递适当的错误详细信息,可以重构错误处理程序中间件:
const errorHandler = (error, req, res, next) => {
console.log(error);
if (error.name === "ValidationError") {
return res.status(400).send({
type: "ValidationError",
details: error.details,
});
}
res.status(400).send(error.message);
};
module.exports = errorHandler;
errorHandler.js
现在自定义,当我们使用传递给的空对象发出相同的请求时 validate()
方法:
我们现在可以访问以更易读/友好的方式返回消息的自定义对象。 这样,我们就可以根据传入的错误类型来发送和处理不同类型的错误。
结论
在本指南中,我们介绍了 Express.js 错误处理的各个方面,包括默认情况下如何处理同步和异步代码、如何创建自己的错误类、如何编写自定义错误处理中间件函数并提供 next
作为最终的捕获处理程序
与所有任务一样,在开发过程中也有最佳实践,包括有效的错误处理,今天我们已经了解了如何以稳健的方式处理 Express 应用程序中的错误。
正确处理错误不仅意味着通过轻松发现错误和错误来缩短开发时间,还意味着为大型应用程序开发健壮的代码库。 在本指南中,我们了解了如何设置中间件来处理操作错误。 改进错误处理的一些其他方法包括:不发送堆栈跟踪、优雅地停止进程以处理未捕获的异常、提供适当的错误消息、发送错误日志以及设置一个扩展类 Error
类。
我希望我在本教程中使用的示例对您来说是愉快的。 我介绍了在编写用于现实世界的错误管理 Express 应用程序时可能遇到的各种场景。 如果我遗漏了什么,请告诉我。 这将使我们受益,也能帮助我学到更多。 祝你有美好的一天,感谢阅读。
您可以参考文章中使用的所有源代码 Github上.