Skip to content

ExpressJS - 错误处理

健壮的错误处理是任何生产应用程序的关键组成部分。它能防止服务器崩溃,有助于调试,并向用户提供有意义的反馈,同时不暴露敏感的实现细节。

在 Express 中,错误处理通过一种特殊类型的中间件来完成。这个中间件函数有四个参数,而不是通常的三个:(err, req, res, next)。Express 就是通过这种签名来识别它是一个错误处理中间件的。

这个中间件应该在所有其他 app.use() 和路由调用之后最后定义。

app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('出错了!');
});

你可以通过两种方式触发错误处理中间件:

  • **同步错误:** 如果在路由处理程序或中间件中的同步代码中抛出错误,Express 将自动捕获它并将其传递给错误处理器。
  • **异步错误:** 对于异步代码中的错误(如数据库查询或 API 调用),你必须捕获它们并将其传递给 next()。

在此示例中,我们手动创建一个错误并将其传递给 next(err)。这会跳过所有后续的非错误处理中间件和路由,直接跳转到我们的错误处理器。

import express from 'express';
const app = express();
app.get('/problem', (req, res, next) => {
// 模拟一个错误
const err = new Error('这是一个模拟错误。');
err.status = 418; // 你可以添加自定义属性
// 将错误传递给下一个中间件
next(err);
});
// 在此处定义其他路由和中间件...
app.get('/', (req, res) => res.send('欢迎!'));
// 集中式错误处理器(必须是最后一个)
app.use((err, req, res, next) => {
console.error('发生错误:', err.message);
// 如果未提供状态码,则设置默认值
const statusCode = err.status || 500;
res.status(statusCode).json({
error: {
message: err.message || '发生了内部服务器错误。'
}
});
});
app.listen(3000, () => console.log('服务器正在端口 3000 上运行'));

当你访问 http://localhost:3000/problem 时,浏览器将显示一个带有 418 状态码的 JSON 响应:

{
"error": {
"message": "这是一个模拟错误。"
}
}

在 async/await 函数中处理错误需要一个 try...catch 块来将错误传递给 next()。

import { findUserById } from './db'; // 假设这是一个异步数据库函数
app.get('/users/:id', async (req, res, next) => {
try {
const user = await findUserById(req.params.id);
if (!user) {
// 为“未找到”创建一个特定错误
const err = new Error('未找到用户');
err.status = 404;
return next(err);
}
res.json(user);
} catch (error) {
// 捕获数据库错误或其他异常
next(error);
}
});

现代开发提示: 像 express-async-errors 这样的库可以自动为你处理这些。只需导入该库,你就可以在异步路由处理程序中消除对 try...catch 块的需求。

  • **集中化错误处理:** 使用一个单一的、集中式的错误处理中间件,以实现一致的错误响应。
  • **不要泄露实现细节:** 在生产环境中,切勿将完整的错误堆栈跟踪发送给客户端。记录它用于调试,但发送一个通用、用户友好的消息。
  • **创建自定义错误类:** 为了更结构化的错误处理,扩展内置的 Error 类来创建自定义错误类型(例如 ApiError、NotFoundError),这些错误类型可以包含状态码和其他元数据。
  • **处理未捕获的异常:** 为了最终的稳定性,监听 process.on('uncaughtException') 和 process.on('unhandledRejection') 来捕获 Express 请求-响应周期之外的错误。记录错误并优雅地关闭服务器。