Express 错误处理
Express 应用健壮的错误处理:自定义错误类、异步包装器、集中错误中间件和环境感知响应。
1. 自定义错误类
class AppError extends Error {
constructor(message, statusCode = 500, code = null) {
super(message);
this.statusCode = statusCode;
this.code = code;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
class NotFoundError extends AppError {
constructor(resource = '资源') {
super(`${resource}不存在`, 404, 'NOT_FOUND');
}
}
class ValidationError extends AppError {
constructor(message, fields = {}) {
super(message, 422, 'VALIDATION_ERROR');
this.fields = fields;
}
}
module.exports = { AppError, NotFoundError, ValidationError };
2. 异步错误处理
// 工具包装器 — 路由中无需 try/catch
const asyncHandler = fn => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
// 使用
router.get('/users/:id', asyncHandler(async (req, res) => {
const user = await db.users.findById(req.params.id);
if (!user) throw new NotFoundError('用户');
res.json(user);
}));
3. 集中错误中间件
function errorHandler(err, req, res, next) {
if (err.name === 'ValidationError') {
const fields = Object.fromEntries(
Object.entries(err.errors).map(([k, v]) => [k, v.message])
);
return res.status(422).json({ error: '验证失败', fields });
}
if (err.name === 'JsonWebTokenError') {
return res.status(401).json({ error: 'token 无效' });
}
if (err.isOperational) {
const body = { error: err.message, code: err.code };
if (err.fields) body.fields = err.fields;
return res.status(err.statusCode).json(body);
}
console.error('未处理错误:', err);
const message = process.env.NODE_ENV === 'production'
? '服务器内部错误' : err.message;
res.status(500).json({ error: message });
}
// 必须最后注册
app.use(errorHandler);
4. 404 处理器
// 放在所有路由之后、errorHandler 之前
app.use((req, res, next) => {
next(new NotFoundError(`路由 ${req.method} ${req.path}`));
});
5. 未处理的拒绝与异常
process.on('unhandledRejection', (reason) => {
console.error('未处理的 Promise 拒绝:', reason);
server.close(() => process.exit(1));
});
process.on('uncaughtException', (err) => {
console.error('未捕获的异常:', err);
process.exit(1);
});
process.on('SIGTERM', () => {
server.close(() => process.exit(0));
});
6. HTTP 状态码参考
| 代码 | 名称 | 使用场景 |
|---|---|---|
| 200 | OK | 成功的 GET/PUT/PATCH |
| 201 | Created | 成功创建资源 |
| 204 | No Content | 成功删除 |
| 400 | Bad Request | 请求格式错误 |
| 401 | Unauthorized | 需要身份验证 |
| 403 | Forbidden | 已认证但无权限 |
| 404 | Not Found | 资源不存在 |
| 422 | Unprocessable Entity | 验证错误 |
| 429 | Too Many Requests | 超出限流 |
| 500 | Internal Server Error | 服务器意外错误 |