BackendErrorHandlingPatterns BackendErrorHandlingPatterns

后端API的错误处理策略,包括错误分类、自定义错误类、中间件、日志记录、监控和恢复模式

后端开发 0 次安装 0 次浏览 更新于 3/5/2026

后端错误处理模式

概览

错误处理是后端开发中的关键组成部分,它使应用程序能够可靠、可维护和可调试地运行。它包括处理、记录和监控错误的模式和最佳实践。

错误处理包括:

  • 错误分类 - 区分错误类型(操作性 vs 程序员错误)
  • 自定义错误类 - 创建特定于领域的错误类
  • 错误中间件 - 用于一致响应的全局错误处理程序
  • 错误记录 - 记录错误以便于调试和监控
  • 错误监控 - 外部监控服务(Sentry等)
  • 错误恢复 - 重试、回退和断路器模式

为什么这很重要

  • 减少停机时间:正确的错误处理可以减少40-60%的停机时间
  • 改善用户体验:优雅的错误处理增强了用户体验
  • 减少调试时间:结构化的错误记录减少了调试时间
  • 提高可维护性:一致的错误处理提高了可维护性
  • 增强可靠性:错误恢复模式增加了可靠性

核心概念

1. 错误分类

操作性错误(预期的):

  • 网络超时
  • 数据库连接失败
  • 无效的用户输入
  • 速率限制超出
  • 外部服务不可用

程序员错误(意外的):

  • 未定义的变量
  • 类型错误
  • 内存泄漏
  • 逻辑错误
  • 竞态条件

2. 自定义错误类

// 基础错误类
export class AppError extends Error {
  constructor(
    public statusCode: number,
    public message: string,
    public isOperational: boolean = true
  ) {
    super(message)
    this.name = this.constructor.name
    Error.captureStackTrace(this, this.constructor)
  }
}

// 特定错误类型
export class ValidationError extends AppError {
  constructor(message: string = "验证失败") {
    super(422, message)
  }
}

export class NotFoundError extends AppError {
  constructor(resource: string = "资源") {
    super(404, `${resource} 未找到`)
  }
}

3. 错误中间件

// 全局错误处理器
export function errorHandler(
  error: Error,
  req: Request,
  res: Response,
  next: NextFunction
): void {
  // 记录错误
  logger.error(error, { path: req.path, method: req.method })

  // 处理操作性错误
  if (error instanceof AppError) {
    return res.status(error.statusCode).json({
      success: false,
      message: error.message,
    })
  }

  // 处理程序员错误(不要在生产中暴露)
  res.status(500).json({
    success: false,
    message: process.env.NODE_ENV === "production"
      ? "内部服务器错误"
      : error.message,
  })
}

4. 错误记录

// 使用Winston进行结构化日志记录
const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || "info",
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: "logs/error.log", level: "error" }),
  ],
})

5. 错误监控

// Sentry集成
Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV || "development",
  beforeSend(event) {
    // 过滤操作性错误
    if (event.exception?.values?.[0]?.mechanism?.type === "express") {
      const error = event.request?.error as any
      if (error?.isOperational) {
        return null
      }
    }
    return event
  },
})

6. 错误恢复模式

带有指数退避的重试:

export async function retry<T>(
  fn: () => Promise<T>,
  options: { maxAttempts?: number; delay?: number; backoff?: boolean } = {}
): Promise<T> {
  const { maxAttempts = 3, delay = 1000, backoff = true } = options

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn()
    } catch (error) {
      if (attempt === maxAttempts) throw error
      const waitTime = backoff ? delay * attempt : delay
      await new Promise(resolve => setTimeout(resolve, waitTime))
    }
  }

  throw new Error("超过最大重试次数")
}

回退策略:

export async function withFallback<T>(
  primaryFn: () => Promise<T>,
  fallbackFn: () => Promise<T>,
  shouldFallback?: (error: Error) => boolean
): Promise<T> {
  try {
    return await primaryFn()
  } catch (error) {
    if (shouldFallback && !shouldFallback(error)) {
      throw error
    }
    return await fallbackFn()
  }
}

断路器:

export class CircuitBreaker {
  private failures = 0
  private state: "closed" | "open" | "half-open" = "closed"

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === "open") {
      throw new Error("断路器已打开")
    }

    try {
      const result = await fn()
      this.onSuccess()
      return result
    } catch (error) {
      this.onFailure()
      throw error
    }
  }

  private onSuccess(): void {
    this.failures = 0
    this.state = "closed"
  }

  private onFailure(): void {
    this.failures++
    if (this.failures >= 5) {
      this.state = "open"
    }
  }
}

快速开始

  1. 创建错误类:

    export class AppError extends Error {
      constructor(public statusCode: number, public message: string) {
        super(message)
        this.name = this.constructor.name
      }
    }
    
  2. 设置错误中间件:

    app.use(errorHandler)
    
  3. 配置日志记录:

    import winston from "winston"
    export const logger = winston.createLogger({ ... })
    
  4. 添加监控:

    npm install @sentry/node
    
  5. 实现恢复模式:

    await retry(() => fetchFromAPI(url), { maxAttempts: 3 })
    

生产清单

  • [ ] 自定义错误类用于特定领域的错误
  • [ ] 将错误分类为操作性 vs 程序员错误
  • [ ] 实现全局错误中间件
  • [ ] 使用一致的错误响应格式
  • [ ] 使用结构化日志记录错误
  • [ ] 使用外部服务监控错误(Sentry等)
  • [ ] 实施错误恢复模式
  • [ ] 对外部服务使用断路器
  • [ ] 测试错误场景
  • [ ] 为生产环境清理错误消息
  • [ ] 实施瞬态错误的重试逻辑
  • [ ] 对关键故障使用回退策略
  • [ ] 记录错误代码和消息
  • [ ] 设置关键错误的警报
  • [ ] 定期审查错误日志

反模式

  1. 吞食错误:不要捕获并忽略错误
  2. 暴露堆栈跟踪:不要在生产中暴露堆栈跟踪
  3. 错误消息差:不要使用难以理解的错误消息
  4. 缺少日志记录:不要适当地记录错误
  5. 无监控:不要在没有错误监控的情况下运行
  6. 恢复差:不要未能实施错误恢复

集成点

  • 验证03-backend-api/validation
  • 中间件03-backend-api/middleware
  • 监控14-monitoring-observability
  • 数据库04-database
  • 测试16-testing

进一步阅读