Express 中的错误处理
我刚开始学习Express时,处理错误非常困难。似乎没有人写出我需要的答案,所以我只能自己摸索学习。
今天,我想分享我所了解的关于在 Express 应用中处理错误的所有知识。
我们先从同步错误开始。
处理同步错误
如果要处理同步错误,可以throw在 Express 请求处理程序中处理该错误。(注:请求处理程序也称为控制器。我更喜欢称其为请求处理程序,因为它更明确、更容易理解。)
app.post("/testing", (req, res) => {
throw new Error("Something broke! 😱");
});
这些错误可以通过 Express 错误处理程序捕获。如果您没有编写自定义错误处理程序(下文将详细介绍),Express 将使用默认错误处理程序来处理这些错误。
Express 的默认错误处理程序将:
- 将 HTTP 状态设置为 500
- 向请求者发送文本回复
- 将文本响应记录到控制台。
处理异步错误
如果要处理异步错误,需要通过next参数将错误发送到 Express 错误处理程序。
app.post("/testing", async (req, res, next) => {
return next(new Error("Something broke again! 😱"));
});
如果你在 Express 应用中使用 Async/await,最好使用像express-async-handler这样的包装函数。这样你就可以编写异步代码而无需使用 try/catch 块。我在“在 Express 中使用 Async/await ”这篇文章中对此有更详细的介绍。
const asyncHandler = require("express-async-handler");
app.post(
"/testing",
asyncHandler(async (req, res, next) => {
// Do something
})
);
一旦你用 `<express>` 包裹了请求处理程序express-async-handler,你就可以throw像以前一样处理错误,它将由 Express 错误处理程序处理。
app.post(
"/testing",
asyncHandler(async (req, res, next) => {
throw new Error("Something broke yet again! 😱");
})
);
编写自定义错误处理程序
Express 错误处理程序接受四个参数:
errorreqresnext
它们必须放在所有中间件和路由之后。
app.use(/*...*/);
app.get(/*...*/);
app.post(/*...*/);
app.put(/*...*/);
app.delete(/*...*/);
// Place your error handler after all other middlewares
app.use((error, req, res, next) => {
/* ... */
});
一旦您创建了自定义错误处理程序,Express 将停止使用其默认错误处理程序。要处理错误,您需要与请求该端点的前端进行通信。这意味着您需要:
- 发送有效的 HTTP 状态码
- 请发送有效回复
有效的 HTTP 状态码取决于具体情况。以下列出一些常见错误,您应该做好准备:
- 400 错误请求:
- 用于用户未填写某个字段的情况(例如,付款表单中未填写信用卡信息)。
- 当用户输入错误信息时也会使用此功能(例如:在密码字段和密码确认字段中输入不同的密码)。
- 401 未授权错误:当用户输入错误的登录信息(例如用户名、电子邮件或密码)时使用。
- 403 禁止访问错误:当用户无权访问端点时使用。
- 404 未找到错误:当找不到端点时使用。
- 500 内部服务器错误:前端发送的请求是正确的,但后端出现错误。
确定正确的 HTTP 状态代码后,您需要使用以下命令设置状态:res.status
app.use((error, req, res, next) => {
// Bad request error
res.status(400);
res.json(/* ... */);
});
HTTP 状态码应与错误消息匹配。要使状态码与错误消息匹配,必须将状态码与错误消息一起发送。
最简单的方法是使用http-errors包。它允许你在错误信息中发送三项内容:
- 状态码
- 错误信息
- 您想发送的任何属性。此项为可选。
安装中http-errors:
npm install http-errors --save
使用http-errors:
const createError = require("http-errors");
// Creating an error
throw createError(status, message, properties);
我们一起来看一个例子,以便更清楚地理解。假设你尝试通过电子邮件地址查找用户,但找不到该用户。你想抛出一个错误信息:“找不到用户”。
创建错误时,您需要:
- 发送 400 错误请求(因为用户填写了错误信息)。将此错误作为第一个参数发送。
- 发送一条消息,内容为“未找到用户”。将此消息作为第二个参数发送。
app.put(
"/testing",
asyncHandler(async (req, res) => {
const { email } = req.body;
const user = await User.findOne({ email });
// Throws error if user not found
if (!user) throw createError(400, `User '${email}' not found`);
})
);
您可以使用以下命令获取状态码error.status,并使用以下命令获取错误消息error.message。
// Logging the error
app.use((error, req, res, next) => {
console.log("Error status: ", error.status);
console.log("Message: ", error.message);
});
然后,您可以使用 . 设置错误状态res.status。您可以使用 . 发送消息res.json。
app.use((error, req, res, next) => {
// Sets HTTP status code
res.status(error.status);
// Sends response
res.json({ message: error.message });
});
我个人喜欢同时发送状态、消息和堆栈跟踪信息,以便于调试。
app.use((error, req, res, next) => {
// Sets HTTP status code
res.status(error.status);
// Sends response
res.json({
status: error.status,
message: error.message,
stack: error.stack
});
});
回退状态码
如果错误并非源自此处createError,则不会有状态属性。
举个例子。假设你尝试用某个函数读取一个文件fs.readFile,但是该文件不存在。
const fs = require('fs')
const util = require('util')
// Converts readFile from callbacks to Async/await.
// Find out how to do this here: https://zellwk.comhttps://zellwk.com/blog/callbacks-to-promises
const readFilePromise = util.promisify(fs.readFile)
app.get('/testing', asyncHandler(async (req, res, next) => {
const data = await readFilePromise('some-file')
})
此错误不包含status属性。
app.use((error, req, res, next) => {
console.log("Error status: ", error.status);
console.log("Message: ", error.message);
});
在这种情况下,您可以默认返回 500 内部服务器错误。
app.use((error, req, res, next) => {
res.status(error.status || 500);
res.json({
status: error.status,
message: error.message,
stack: error.stack
});
});
更改错误状态代码
假设你想从用户输入中检索一个文件。如果该文件不存在,你应该抛出 400 错误请求,因为这不是服务器的错。
在这种情况下,您需要使用它try/catch来捕获原始错误。然后,您可以使用它重新创建一个错误createError。
app.get('/testing', asyncHandler(async (req, res, next) => {
try {
const { file } = req.body
const contents = await readFilePromise(path.join(__dirname, file))
} catch (error) {
throw createError(400, `File ${file} does not exist`)
}
})
处理 404 错误
如果请求在所有中间件和路由中都失败,则找不到端点。
要处理“未找到”错误,需要在路由和错误处理程序之间插入一个中间件。在这里,创建一个错误createError。
// Middlewares...
// Routes...
app.use((req, res, next) => {
next(createError(404));
});
// Error handler...
关于“无法在将标头发送给客户端后设置标头”
如果看到“无法在标头发送到服务器后设置标头”的错误提示,请不要惊慌。
出现此错误的原因是代码在同一个处理程序中多次运行了设置响应头的方法。以下是设置响应头的方法:
res.sendres.jsonres.renderres.sendFileres.sendStatusres.endres.redirect
例如,如果您在同一个响应处理程序中运行res.render并执行res.json,则会收到“发送标头后无法设置标头”错误。
app.get("/testing", (req, res) => {
res.render("new-page");
res.json({ message: "¯_(ツ)_/¯" });
});
所以,如果出现此错误,请仔细检查您的响应处理程序,确保不会运行上述方法两次。
流媒体播放时
如果在向前端发送响应流时发生错误,您将收到相同的“无法设置标头”错误。
在这种情况下,Express 建议您将错误处理委托给默认的 Express 处理程序。它会自动发送错误并关闭连接。
app.use((error, req, res, next) => {
// Do this only if you're streaming a response
if (res.headersSent) {
return next(error);
}
// Rest of the error handlers
});
目前我只知道这些!:)
感谢阅读。本文最初发布在我的博客上。如果您想阅读更多帮助您成为更优秀的前端开发人员的文章,请订阅我的邮件列表。
文章来源:https://dev.to/zellwk/handling-errors-in-express-5b85







