Appearance
Node.js 错误处理
错误处理是Node.js应用开发中的重要组成部分。本章详细介绍Node.js中的错误处理机制和最佳实践。
错误类型
JavaScript错误
javascript
// 语法错误 (SyntaxError)
// const a = ; // 语法错误,会导致程序启动失败
// 引用错误 (ReferenceError)
try {
console.log(undefinedVariable); // ReferenceError
} catch (err) {
console.error('引用错误:', err.message);
}
// 类型错误 (TypeError)
try {
const obj = null;
obj.method(); // TypeError
} catch (err) {
console.error('类型错误:', err.message);
}
// 范围错误 (RangeError)
try {
const arr = new Array(-1); // RangeError
} catch (err) {
console.error('范围错误:', err.message);
}
系统错误
javascript
const fs = require('fs');
// 系统错误示例
fs.readFile('/nonexistent/file.txt', (err, data) => {
if (err) {
console.error('系统错误:', err.code); // 例如: ENOENT (文件不存在)
console.error('错误信息:', err.message);
console.error('错误堆栈:', err.stack);
}
});
错误处理策略
同步错误处理
javascript
// 使用try-catch处理同步错误
function syncOperation() {
try {
// 可能抛出错误的代码
const result = JSON.parse('invalid json');
return result;
} catch (err) {
console.error('同步操作错误:', err.message);
return null;
}
}
// 自定义错误处理
function processData(data) {
if (!data) {
throw new Error('数据不能为空');
}
if (typeof data !== 'object') {
throw new TypeError('数据必须是对象类型');
}
return data;
}
try {
const result = processData(null);
console.log(result);
} catch (err) {
if (err instanceof TypeError) {
console.error('类型错误:', err.message);
} else {
console.error('一般错误:', err.message);
}
}
异步错误处理
回调函数中的错误处理
javascript
// 错误优先回调模式
function asyncOperation(data, callback) {
setTimeout(() => {
if (!data) {
callback(new Error('数据不能为空'));
return;
}
if (typeof data !== 'string') {
callback(new TypeError('数据必须是字符串类型'));
return;
}
callback(null, `处理结果: ${data}`);
}, 1000);
}
// 使用错误优先回调
asyncOperation('test', (err, result) => {
if (err) {
console.error('异步操作失败:', err.message);
return;
}
console.log('异步操作成功:', result);
});
Promise中的错误处理
javascript
// Promise错误处理
function asyncOperationPromise(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (!data) {
reject(new Error('数据不能为空'));
return;
}
if (typeof data !== 'string') {
reject(new TypeError('数据必须是字符串类型'));
return;
}
resolve(`处理结果: ${data}`);
}, 1000);
});
}
// 使用Promise的catch方法
asyncOperationPromise('test')
.then(result => {
console.log('成功:', result);
})
.catch(err => {
console.error('失败:', err.message);
});
// 使用async/await
async function handleAsyncOperation() {
try {
const result = await asyncOperationPromise('test');
console.log('成功:', result);
} catch (err) {
console.error('失败:', err.message);
}
}
全局错误处理
未捕获异常
javascript
// 监听未捕获的异常
process.on('uncaughtException', (err) => {
console.error('未捕获的异常:', err);
console.error('错误堆栈:', err.stack);
// 在生产环境中,通常需要优雅地关闭应用
// process.exit(1);
});
// 监听未处理的Promise拒绝
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的Promise拒绝:', reason);
console.error('Promise:', promise);
// 记录错误,但不退出应用
// 在某些情况下,可能需要退出应用
});
// 示例:触发未处理的Promise拒绝
async function badAsyncFunction() {
throw new Error('这是一个未处理的错误');
}
// 如果不使用await或catch,这将触发unhandledRejection
badAsyncFunction();
进程退出处理
javascript
// 监听进程退出信号
process.on('SIGTERM', () => {
console.log('收到SIGTERM信号,正在优雅关闭...');
// 执行清理操作
process.exit(0);
});
process.on('SIGINT', () => {
console.log('收到SIGINT信号 (Ctrl+C),正在关闭...');
// 执行清理操作
process.exit(0);
});
// 监听进程退出
process.on('exit', (code) => {
console.log(`进程即将退出,退出码: ${code}`);
});
Express中的错误处理
中间件错误处理
javascript
const express = require('express');
const app = express();
// 使用中间件解析JSON
app.use(express.json());
// 业务逻辑中间件
const validateUser = (req, res, next) => {
const { name, email } = req.body;
if (!name || !email) {
// 创建自定义错误
const error = new Error('姓名和邮箱是必需的');
error.status = 400;
return next(error);
}
next();
};
// 路由
app.post('/users', validateUser, (req, res) => {
res.json({ message: '用户创建成功' });
});
// 自定义错误类
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
// 使用自定义错误类
app.get('/users/:id', (req, res, next) => {
const userId = parseInt(req.params.id);
if (isNaN(userId)) {
return next(new AppError('无效的用户ID', 400));
}
if (userId <= 0) {
return next(new AppError('用户ID必须大于0', 400));
}
// 模拟用户查找
const user = { id: userId, name: '测试用户' };
res.json(user);
});
// 全局错误处理中间件
app.use((err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
if (process.env.NODE_ENV === 'development') {
res.status(err.statusCode).json({
status: err.status,
error: err,
message: err.message,
stack: err.stack
});
} else {
if (err.isOperational) {
// 可操作的错误(如验证错误、数据库错误等)
res.status(err.statusCode).json({
status: err.status,
message: err.message
});
} else {
// 编程错误或未知错误
console.error('未知错误:', err);
res.status(500).json({
status: 'error',
message: '服务器内部错误'
});
}
}
});
// 404处理
app.use('*', (req, res, next) => {
const err = new AppError(`无法找到 ${req.originalUrl}`, 404);
next(err);
});
错误记录和监控
基础错误日志
javascript
const fs = require('fs');
const path = require('path');
class Logger {
constructor(logFile) {
this.logFile = logFile || path.join(__dirname, 'error.log');
}
logError(error, context = {}) {
const timestamp = new Date().toISOString();
const logEntry = {
timestamp,
message: error.message,
stack: error.stack,
context
};
const logLine = JSON.stringify(logEntry) + '\n';
fs.appendFileSync(this.logFile, logLine);
}
logInfo(message, context = {}) {
const timestamp = new Date().toISOString();
const logEntry = {
timestamp,
level: 'INFO',
message,
context
};
const logLine = JSON.stringify(logEntry) + '\n';
fs.appendFileSync(this.logFile, logLine);
}
}
const logger = new Logger();
// 使用错误记录器
function safeOperation() {
try {
// 可能出错的操作
throw new Error('测试错误');
} catch (err) {
logger.logError(err, {
operation: 'safeOperation',
userId: 123
});
}
}
高级错误处理中间件
javascript
// 错误处理中间件
const errorMiddleware = (err, req, res, next) => {
// 记录错误
console.error({
timestamp: new Date().toISOString(),
error: err.message,
stack: err.stack,
url: req.url,
method: req.method,
headers: req.headers,
body: req.body
});
// 根据错误类型返回不同的响应
if (err.type === 'entity.parse.failed') {
return res.status(400).json({
error: '无效的JSON格式'
});
}
if (err.code === 'LIMIT_FILE_SIZE') {
return res.status(413).json({
error: '文件过大'
});
}
// 默认错误响应
res.status(err.statusCode || 500).json({
error: process.env.NODE_ENV === 'production'
? '服务器内部错误'
: err.message
});
};
自定义错误类
javascript
// 基础自定义错误类
class CustomError extends Error {
constructor(message, statusCode) {
super(message);
this.name = this.constructor.name;
this.statusCode = statusCode;
// 保持错误堆栈追踪
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
}
}
// 业务特定错误类
class ValidationError extends CustomError {
constructor(message) {
super(message, 400);
this.type = 'VALIDATION_ERROR';
}
}
class AuthenticationError extends CustomError {
constructor(message = '认证失败') {
super(message, 401);
this.type = 'AUTHENTICATION_ERROR';
}
}
class AuthorizationError extends CustomError {
constructor(message = '权限不足') {
super(message, 403);
this.type = 'AUTHORIZATION_ERROR';
}
}
class NotFoundError extends CustomError {
constructor(message = '资源未找到') {
super(message, 404);
this.type = 'NOT_FOUND_ERROR';
}
}
class DatabaseError extends CustomError {
constructor(message = '数据库错误') {
super(message, 500);
this.type = 'DATABASE_ERROR';
}
}
// 使用自定义错误
function validateUserInput(data) {
if (!data.email) {
throw new ValidationError('邮箱是必需的');
}
if (!data.password) {
throw new ValidationError('密码是必需的');
}
if (data.password.length < 6) {
throw new ValidationError('密码长度至少为6位');
}
}
// 错误处理示例
function handleUserRegistration(userData) {
try {
validateUserInput(userData);
// 继续处理用户注册逻辑
} catch (err) {
if (err instanceof ValidationError) {
console.log('验证错误:', err.message);
return { success: false, error: err.message, type: err.type };
}
// 其他错误类型处理
console.error('用户注册失败:', err);
return { success: false, error: '注册失败', type: 'UNKNOWN_ERROR' };
}
}
异步操作错误处理
并行操作错误处理
javascript
// 处理多个Promise的错误
async function handleParallelOperations() {
const promises = [
asyncOperation1(),
asyncOperation2(),
asyncOperation3()
];
try {
// 如果任何一个Promise失败,整个Promise.all会失败
const results = await Promise.all(promises);
return results;
} catch (err) {
console.error('并行操作失败:', err.message);
throw err;
}
}
// 使用Promise.allSettled处理部分失败
async function handleParallelOperationsWithPartialFailure() {
const promises = [
asyncOperation1(),
asyncOperation2(),
asyncOperation3()
];
const results = await Promise.allSettled(promises);
const successful = results
.filter(result => result.status === 'fulfilled')
.map(result => result.value);
const failed = results
.filter(result => result.status === 'rejected')
.map(result => result.reason);
return { successful, failed };
}
超时错误处理
javascript
// 带超时的Promise包装器
function withTimeout(promise, timeoutMs) {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
reject(new Error(`操作超时 (${timeoutMs}ms)`));
}, timeoutMs);
promise
.then(result => {
clearTimeout(timeoutId);
resolve(result);
})
.catch(err => {
clearTimeout(timeoutId);
reject(err);
});
});
}
// 使用示例
async function operationWithTimeout() {
try {
const result = await withTimeout(
fetch('https://api.example.com/data'),
5000 // 5秒超时
);
console.log('操作成功:', result);
} catch (err) {
if (err.message.includes('超时')) {
console.error('操作超时');
} else {
console.error('操作失败:', err.message);
}
}
}
错误恢复策略
重试机制
javascript
// 重试函数
async function retryAsyncOperation(operation, maxRetries = 3, delay = 1000) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const result = await operation();
return result;
} catch (err) {
lastError = err;
if (attempt === maxRetries) {
break;
}
console.log(`操作失败,第${attempt}次重试...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
// 使用重试机制
async function unreliableOperation() {
// 模拟不稳定的操作
if (Math.random() < 0.7) { // 70%概率失败
throw new Error('操作失败');
}
return '操作成功';
}
async function useRetry() {
try {
const result = await retryAsyncOperation(unreliableOperation, 3, 2000);
console.log(result);
} catch (err) {
console.error('所有重试都失败了:', err.message);
}
}
熔断器模式
javascript
class CircuitBreaker {
constructor(options = {}) {
this.failureThreshold = options.failureThreshold || 5;
this.timeout = options.timeout || 60000; // 60秒
this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
this.failureCount = 0;
this.lastFailureTime = null;
}
async call(operation) {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime > this.timeout) {
this.state = 'HALF_OPEN';
} else {
throw new Error('熔断器开启,拒绝请求');
}
}
try {
const result = await operation();
this.onSuccess();
return result;
} catch (err) {
this.onFailure();
throw err;
}
}
onSuccess() {
this.failureCount = 0;
this.state = 'CLOSED';
}
onFailure() {
this.failureCount++;
if (this.failureCount >= this.failureThreshold) {
this.state = 'OPEN';
this.lastFailureTime = Date.now();
}
}
}
// 使用熔断器
const circuitBreaker = new CircuitBreaker({
failureThreshold: 3,
timeout: 30000
});
async function protectedOperation() {
return circuitBreaker.call(async () => {
// 可能失败的操作
if (Math.random() < 0.6) {
throw new Error('操作失败');
}
return '操作成功';
});
}
正确的错误处理是构建健壮Node.js应用的关键,通过适当的错误处理策略,可以提高应用的可靠性和用户体验。