关于 error-handling
面向 Node.js 开发者的错误处理学习技能,涵盖从自定义异常到 Express 中间件及进程级故障的处理。内容还包括重试逻辑、响应格式、测试和故障排查。
主要功能
- 01自定义类型化错误类
- 02同步与异步错误处理
- 03Express 错误中间件
- 04进程级错误处理器
- 05指数退避重试
- 06测试与故障排查指南
适用场景
- 01构建健壮的 Node.js 应用
- 02集中处理 Express API 错误
- 03处理数据库或外部 API 调用失败
- 04测试错误行为与恢复逻辑
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ pluginagentmarketplace/ custom-plugin-nodejs --skill error-handling安装后在 Claude Code 中输入/error-handling确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/config.yamlschema.jsonreferences/GUIDE.mdPATTERNS.mdscripts/validate.py详细介绍
Node.js 错误处理技能
掌握错误处理模式,构建能够优雅地处理失败并提供有意义反馈的弹性 Node.js 应用程序。
快速入门
错误处理分为 4 层:
- 自定义错误类 - 类型明确、信息丰富的错误
- Try/Catch 模式 - 同步和异步处理
- Express 中间件 - 集中处理 API 错误
- 进程处理器 - 捕获未处理的异常
核心概念
自定义错误类
// Base application error
class AppError extends Error {
constructor(message, statusCode, code) {
super(message);
this.statusCode = statusCode;
this.code = code;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
// Specific error types
class ValidationError extends AppError {
constructor(message, details = []) {
super(message, 400, 'VALIDATION_ERROR');
this.details = details;
}
}
class NotFoundError extends AppError {
constructor(resource = 'Resource') {
super(`${resource} not found`, 404, 'NOT_FOUND');
}
}
class UnauthorizedError extends AppError {
constructor(message = 'Authentication required') {
super(message, 401, 'UNAUTHORIZED');
}
}
class ForbiddenError extends AppError {
constructor(message = 'Access denied') {
super(message, 403, 'FORBIDDEN');
}
}
异步错误处理
// Async wrapper for Express routes
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Usage
router.get('/users/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) throw new NotFoundError('User');
res.json(user);
}));
Express 错误中间件
const errorHandler = (err, req, res, next) => {
// Log error
logger.error({
message: err.message,
stack: err.stack,
code: err.code,
url: req.originalUrl
});
// Handle operational errors
if (err.isOperational) {
return res.status(err.statusCode).json({
success: false,
error: {
message: err.message,
code: err.code,
...(err.details && { details: err.details })
}
});
}
// Production: hide internal errors
if (process.env.NODE_ENV === 'production') {
return res.status(500).json({
success: false,
error: { message: 'Internal server error', code: 'INTERNAL_ERROR' }
});
}
// Development: show stack
res.status(500).json({
success: false,
error: { message: err.message, stack: err.stack }
});
};
学习路径
初级(1-2 周)
- ✅ 理解 Error 类型
- ✅ 使用 try/catch 处理同步代码
- ✅ 使用 Promise
.catch()处理错误 - ✅ 基础 Express 错误中间件
中级(3-4 周)
- ✅ 自定义错误类
- ✅ 异步错误包装器
- ✅ 集中式错误处理
- ✅ 使用 Winston 记录错误日志
高级(5-6 周)
- ✅ 生产环境错误监控
- ✅ 优雅关闭
- ✅ 断路器模式
- ✅ 错误恢复策略
进程级错误处理
// Uncaught exceptions
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
logger.fatal({ error }, 'Uncaught exception');
process.exit(1);
});
// Unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection:', reason);
logger.error({ reason }, 'Unhandled rejection');
});
// Graceful shutdown
process.on('SIGTERM', async () => {
console.log('SIGTERM received. Graceful shutdown...');
server.close(() => console.log('HTTP server closed'));
await mongoose.connection.close();
process.exit(0);
});
使用指数退避重试
async function retryOperation(fn, options = {}) {
const { maxRetries = 3, delay = 1000, backoff = 2 } = options;
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
if (attempt === maxRetries) throw error;
const waitTime = delay * Math.pow(backoff, attempt - 1);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
}
throw lastError;
}
错误响应格式
// Success
{ "success": true, "data": { ... } }
// Error
{
"success": false,
"error": {
"message": "User not found",
"code": "NOT_FOUND",
"details": [],
"timestamp": "2024-01-15T10:30:00.000Z"
}
}
单元测试模板
describe('Error Handling', () => {
describe('ValidationError', () => {
it('should create error with correct properties', () => {
const error = new ValidationError('Invalid input', [
{ field: 'email', message: 'Required' }
]);
expect(error.statusCode).toBe(400);
expect(error.code).toBe('VALIDATION_ERROR');
expect(error.isOperational).toBe(true);
});
});
describe('asyncHandler', () => {
it('should catch async errors', async () => {
const mockNext = jest.fn();
const handler = asyncHandler(async () => {
throw new Error('Test error');
});
await handler({}, {}, mockNext);
expect(mockNext).toHaveBeenCalledWith(expect.any(Error));
});
});
});
故障排除
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 未处理的拒绝警告 | 缺少 .catch() | 添加进程处理器和本地捕获处理 |
| 错误被悄然吞掉 | 空的 catch 块 | 记录或重新抛出错误 |
| 缺少堆栈跟踪 | 未调用 Error.captureStackTrace | 使用 AppError 基类 |
| 错误导致内存泄漏 | 存储错误引用 | 使用 WeakMap 或进行清理 |
适用场景
在以下情况下使用恰当的错误处理:
- 构建生产环境 Node.js 应用程序
- 使用 Express 创建 REST API
- 处理数据库操作
- 处理外部 API 调用
- 实现重试逻辑
相关技能
- Express REST API(API 错误响应)
- 异步编程(Promise 拒绝)
- 测试与调试(错误测试)
资源
评论
支持 Markdown