错误处理 #

概述 #

良好的错误处理是 API 设计的重要组成部分。清晰、一致的错误响应可以帮助开发者快速定位和解决问题。

text
┌─────────────────────────────────────────────────────────────┐
│                    错误处理的重要性                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  糟糕的错误响应:                                            │
│  {                                                          │
│    "error": "Something went wrong"                          │
│  }                                                          │
│                                                             │
│  问题:                                                      │
│  ❌ 不知道发生了什么错误                                    │
│  ❌ 不知道如何修复                                          │
│  ❌ 无法调试                                                │
│                                                             │
│  良好的错误响应:                                            │
│  {                                                          │
│    "error": {                                               │
│      "code": "VALIDATION_ERROR",                            │
│      "message": "Invalid input data",                       │
│      "details": [                                           │
│        {                                                    │
│          "field": "email",                                  │
│          "message": "Invalid email format"                  │
│        }                                                    │
│      ],                                                     │
│      "requestId": "req-123-456"                             │
│    }                                                        │
│  }                                                          │
│                                                             │
│  优点:                                                      │
│  ✅ 错误类型明确                                            │
│  ✅ 错误原因清晰                                            │
│  ✅ 提供修复建议                                            │
│  ✅ 便于追踪调试                                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

错误响应格式 #

标准错误格式 #

text
┌─────────────────────────────────────────────────────────────┐
│                    标准错误格式                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  基本结构:                                                  │
│  {                                                          │
│    "error": {                                               │
│      "code": "ERROR_CODE",        // 错误码                 │
│      "message": "Human readable message",  // 错误信息      │
│      "details": [...],            // 详细错误信息           │
│      "requestId": "req-xxx",      // 请求追踪 ID            │
│      "documentation": "https://..."  // 文档链接            │
│    }                                                        │
│  }                                                          │
│                                                             │
│  字段说明:                                                  │
│  - code:机器可读的错误码                                   │
│  - message:人类可读的错误信息                              │
│  - details:详细错误信息(可选)                            │
│  - requestId:请求追踪 ID                                   │
│  - documentation:相关文档链接(可选)                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

错误响应示例 #

text
┌─────────────────────────────────────────────────────────────┐
│                    错误响应示例                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  验证错误(400 Bad Request):                               │
│  HTTP/1.1 400 Bad Request                                   │
│  Content-Type: application/json                             │
│                                                             │
│  {                                                          │
│    "error": {                                               │
│      "code": "VALIDATION_ERROR",                            │
│      "message": "Request validation failed",                │
│      "details": [                                           │
│        {                                                    │
│          "field": "email",                                  │
│          "code": "INVALID_FORMAT",                          │
│          "message": "Invalid email format"                  │
│        },                                                   │
│        {                                                    │
│          "field": "password",                               │
│          "code": "MIN_LENGTH",                              │
│          "message": "Password must be at least 8 characters"│
│        }                                                    │
│      ],                                                     │
│      "requestId": "req-550e8400-e29b-41d4-a716"             │
│    }                                                        │
│  }                                                          │
│                                                             │
│  认证错误(401 Unauthorized):                              │
│  HTTP/1.1 401 Unauthorized                                  │
│  WWW-Authenticate: Bearer error="invalid_token"             │
│                                                             │
│  {                                                          │
│    "error": {                                               │
│      "code": "UNAUTHORIZED",                                │
│      "message": "Authentication required",                  │
│      "requestId": "req-550e8400-e29b-41d4-a716"             │
│    }                                                        │
│  }                                                          │
│                                                             │
│  权限错误(403 Forbidden):                                 │
│  HTTP/1.1 403 Forbidden                                     │
│                                                             │
│  {                                                          │
│    "error": {                                               │
│      "code": "FORBIDDEN",                                   │
│      "message": "You don't have permission to access this resource",│
│      "requestId": "req-550e8400-e29b-41d4-a716"             │
│    }                                                        │
│  }                                                          │
│                                                             │
│  资源不存在(404 Not Found):                               │
│  HTTP/1.1 404 Not Found                                     │
│                                                             │
│  {                                                          │
│    "error": {                                               │
│      "code": "RESOURCE_NOT_FOUND",                          │
│      "message": "User not found",                           │
│      "requestId": "req-550e8400-e29b-41d4-a716"             │
│    }                                                        │
│  }                                                          │
│                                                             │
│  冲突错误(409 Conflict):                                  │
│  HTTP/1.1 409 Conflict                                      │
│                                                             │
│  {                                                          │
│    "error": {                                               │
│      "code": "RESOURCE_CONFLICT",                           │
│      "message": "Email already exists",                     │
│      "details": {                                           │
│        "field": "email",                                    │
│        "value": "user@example.com"                          │
│      },                                                     │
│      "requestId": "req-550e8400-e29b-41d4-a716"             │
│    }                                                        │
│  }                                                          │
│                                                             │
│  速率限制(429 Too Many Requests):                         │
│  HTTP/1.1 429 Too Many Requests                             │
│  Retry-After: 60                                            │
│                                                             │
│  {                                                          │
│    "error": {                                               │
│      "code": "RATE_LIMIT_EXCEEDED",                         │
│      "message": "Rate limit exceeded",                      │
│      "details": {                                           │
│        "limit": 100,                                        │
│        "remaining": 0,                                      │
│        "resetAt": "2025-03-29T12:00:00Z",                   │
│        "retryAfter": 60                                     │
│      },                                                     │
│      "requestId": "req-550e8400-e29b-41d4-a716"             │
│    }                                                        │
│  }                                                          │
│                                                             │
│  服务器错误(500 Internal Server Error):                   │
│  HTTP/1.1 500 Internal Server Error                         │
│                                                             │
│  {                                                          │
│    "error": {                                               │
│      "code": "INTERNAL_ERROR",                              │
│      "message": "An unexpected error occurred",             │
│      "requestId": "req-550e8400-e29b-41d4-a716",            │
│      "documentation": "https://docs.example.com/errors"     │
│    }                                                        │
│  }                                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

错误码设计 #

错误码规范 #

text
┌─────────────────────────────────────────────────────────────┐
│                    错误码设计规范                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  命名规范:                                                  │
│  - 使用大写字母和下划线                                     │
│  - 使用 SCREAMING_SNAKE_CASE                                │
│  - 语义清晰,易于理解                                       │
│                                                             │
│  格式建议:                                                  │
│  {CATEGORY}_{SPECIFIC_ERROR}                                │
│                                                             │
│  示例:                                                      │
│  VALIDATION_ERROR          验证错误                         │
│  VALIDATION_REQUIRED       必填字段缺失                     │
│  VALIDATION_INVALID_FORMAT 格式错误                         │
│  AUTH_UNAUTHORIZED         未认证                           │
│  AUTH_TOKEN_EXPIRED        Token 过期                       │
│  RESOURCE_NOT_FOUND        资源不存在                       │
│  RESOURCE_CONFLICT         资源冲突                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

常用错误码 #

text
┌─────────────────────────────────────────────────────────────┐
│                    常用错误码                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  通用错误:                                                  │
│  INTERNAL_ERROR            内部错误                         │
│  BAD_REQUEST               请求格式错误                     │
│  NOT_FOUND                 资源不存在                       │
│  METHOD_NOT_ALLOWED        方法不允许                       │
│                                                             │
│  认证授权错误:                                              │
│  UNAUTHORIZED              未认证                           │
│  TOKEN_EXPIRED             Token 过期                       │
│  TOKEN_INVALID             Token 无效                       │
│  FORBIDDEN                 无权限                           │
│                                                             │
│  验证错误:                                                  │
│  VALIDATION_ERROR          验证失败                         │
│  REQUIRED_FIELD            必填字段缺失                     │
│  INVALID_FORMAT            格式错误                         │
│  INVALID_VALUE             值无效                           │
│  VALUE_TOO_LONG            值过长                           │
│  VALUE_TOO_SHORT           值过短                           │
│                                                             │
│  业务错误:                                                  │
│  RESOURCE_NOT_FOUND        资源不存在                       │
│  RESOURCE_CONFLICT         资源冲突                         │
│  RESOURCE_DELETED          资源已删除                       │
│  OPERATION_FAILED          操作失败                         │
│                                                             │
│  限制错误:                                                  │
│  RATE_LIMIT_EXCEEDED       速率限制                         │
│  QUOTA_EXCEEDED            配额超限                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

错误处理实现 #

错误处理中间件 #

text
┌─────────────────────────────────────────────────────────────┐
│                    错误处理中间件                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  // 自定义错误类                                             │
│  class ApiError extends Error {                             │
│    constructor(statusCode, code, message, details = null) { │
│      super(message);                                        │
│      this.statusCode = statusCode;                          │
│      this.code = code;                                      │
│      this.details = details;                                │
│    }                                                        │
│  }                                                          │
│                                                             │
│  // 常用错误工厂                                             │
│  const Errors = {                                           │
│    badRequest: (message, details) =>                        │
│      new ApiError(400, 'BAD_REQUEST', message, details),    │
│                                                             │
│    unauthorized: (message = 'Authentication required') =>   │
│      new ApiError(401, 'UNAUTHORIZED', message),            │
│                                                             │
│    forbidden: (message = 'Access denied') =>                │
│      new ApiError(403, 'FORBIDDEN', message),               │
│                                                             │
│    notFound: (resource = 'Resource') =>                     │
│      new ApiError(404, 'NOT_FOUND', `${resource} not found`),│
│                                                             │
│    conflict: (message, details) =>                          │
│      new ApiError(409, 'CONFLICT', message, details),       │
│                                                             │
│    validationError: (details) =>                            │
│      new ApiError(422, 'VALIDATION_ERROR',                  │
│        'Validation failed', details),                       │
│                                                             │
│    rateLimitExceeded: (retryAfter) =>                       │
│      new ApiError(429, 'RATE_LIMIT_EXCEEDED',               │
│        'Rate limit exceeded', { retryAfter }),              │
│                                                             │
│    internal: (message = 'Internal server error') =>         │
│      new ApiError(500, 'INTERNAL_ERROR', message)           │
│  };                                                         │
│                                                             │
│  // 错误处理中间件                                           │
│  function errorHandler(err, req, res, next) {               │
│    const requestId = req.id || generateRequestId();         │
│                                                             │
│    // 记录错误日志                                           │
│    console.error({                                          │
│      requestId,                                             │
│      error: err.message,                                    │
│      stack: err.stack,                                      │
│      path: req.path,                                        │
│      method: req.method                                     │
│    });                                                      │
│                                                             │
│    // 处理已知错误                                           │
│    if (err instanceof ApiError) {                           │
│      return res.status(err.statusCode).json({               │
│        error: {                                             │
│          code: err.code,                                    │
│          message: err.message,                              │
│          details: err.details,                              │
│          requestId                                          │
│        }                                                    │
│      });                                                    │
│    }                                                        │
│                                                             │
│    // 处理未知错误                                           │
│    res.status(500).json({                                   │
│      error: {                                               │
│        code: 'INTERNAL_ERROR',                              │
│        message: 'An unexpected error occurred',             │
│        requestId                                            │
│      }                                                      │
│    });                                                      │
│  }                                                          │
│                                                             │
│  // 使用                                                    │
│  app.get('/users/:id', (req, res, next) => {                │
│    const user = findUser(req.params.id);                    │
│    if (!user) {                                             │
│      return next(Errors.notFound('User'));                  │
│    }                                                        │
│    res.json(user);                                          │
│  });                                                        │
│                                                             │
│  app.use(errorHandler);                                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

验证错误处理 #

text
┌─────────────────────────────────────────────────────────────┐
│                    验证错误处理                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  // 验证中间件                                               │
│  function validate(schema) {                                │
│    return (req, res, next) => {                             │
│      const { error, value } = schema.validate(req.body, {   │
│        abortEarly: false,  // 返回所有错误                  │
│        allowUnknown: false                                  │
│      });                                                    │
│                                                             │
│      if (error) {                                           │
│        const details = error.details.map(err => ({          │
│          field: err.path.join('.'),                         │
│          code: err.type.toUpperCase().replace(/\./g, '_'),  │
│          message: err.message                               │
│        }));                                                 │
│                                                             │
│        return next(Errors.validationError(details));        │
│      }                                                      │
│                                                             │
│      req.body = value;                                      │
│      next();                                                │
│    };                                                       │
│  }                                                          │
│                                                             │
│  // 使用                                                    │
│  const userSchema = Joi.object({                            │
│    name: Joi.string().min(2).max(50).required(),            │
│    email: Joi.string().email().required(),                  │
│    password: Joi.string().min(8).required()                 │
│  });                                                        │
│                                                             │
│  app.post('/users', validate(userSchema), createUser);      │
│                                                             │
│  // 错误响应                                                 │
│  {                                                          │
│    "error": {                                               │
│      "code": "VALIDATION_ERROR",                            │
│      "message": "Validation failed",                        │
│      "details": [                                           │
│        {                                                    │
│          "field": "email",                                  │
│          "code": "STRING_EMAIL",                            │
│          "message": "\"email\" must be a valid email"       │
│        },                                                   │
│        {                                                    │
│          "field": "password",                               │
│          "code": "STRING_MIN",                              │
│          "message": "\"password\" length must be at least 8"│
│        }                                                    │
│      ],                                                     │
│      "requestId": "req-xxx"                                 │
│    }                                                        │
│  }                                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

错误处理最佳实践 #

text
┌─────────────────────────────────────────────────────────────┐
│                    错误处理最佳实践                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 使用正确的 HTTP 状态码                                   │
│     - 400:客户端请求错误                                   │
│     - 401:未认证                                           │
│     - 403:无权限                                           │
│     - 404:资源不存在                                       │
│     - 422:验证失败                                         │
│     - 429:速率限制                                         │
│     - 500:服务器错误                                       │
│                                                             │
│  2. 提供有意义的错误信息                                     │
│     ✅ "Email format is invalid"                            │
│     ❌ "Invalid input"                                      │
│                                                             │
│  3. 包含请求追踪 ID                                          │
│     - 便于日志查询和问题定位                                │
│                                                             │
│  4. 不要暴露敏感信息                                         │
│     ❌ "Database connection failed: password incorrect"     │
│     ✅ "Internal server error"                              │
│                                                             │
│  5. 提供文档链接                                             │
│     - 帮助开发者了解更多信息                                │
│                                                             │
│  6. 记录错误日志                                             │
│     - 记录完整的错误信息                                    │
│     - 包含请求上下文                                        │
│                                                             │
│  7. 区分开发和生产环境                                       │
│     - 开发环境:返回详细错误                                │
│     - 生产环境:返回简化错误                                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

错误处理检查清单 #

text
□ 错误响应格式
  □ 使用统一的错误格式
  □ 包含错误码
  □ 包含错误信息
  □ 包含请求追踪 ID

□ HTTP 状态码
  □ 使用正确的状态码
  □ 状态码与错误类型匹配

□ 错误信息
  □ 信息清晰易懂
  □ 提供修复建议
  □ 不暴露敏感信息

□ 错误日志
  □ 记录错误详情
  □ 记录请求上下文
  □ 便于问题追踪

□ 验证错误
  □ 返回所有验证错误
  □ 指明错误字段
  □ 提供具体的错误原因

下一步 #

现在你已经了解了错误处理,接下来学习 最佳实践,深入了解 RESTful API 设计的最佳实践!

最后更新:2026-03-29