Skip to content
On this page

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应用的关键,通过适当的错误处理策略,可以提高应用的可靠性和用户体验。