第36章:错误处理机制
36.1 概述
错误处理机制是构建健壮API服务的核心组成部分。剪映小助手采用了一套完整的错误处理体系,涵盖统一错误响应格式、HTTP状态码映射、异常处理中间件等多个层面,确保系统在面对各种异常情况时能够提供一致、清晰的错误信息。
本章将深入剖析错误处理机制的实现细节,包括如何设计统一的错误响应格式、如何建立错误码与HTTP状态码的映射关系、如何通过中间件实现全局异常捕获,以及如何提供友好的错误信息展示。
36.2 统一错误响应格式设计
36.2.1 错误响应结构定义
剪映小助手定义了标准化的错误响应格式,确保所有API接口在发生错误时返回一致的数据结构:
# src/schemas/base.py
from pydantic import BaseModel
from typing import Optional, Any, Dict
class ErrorResponse(BaseModel):
"""统一错误响应格式"""
code: int
message: str
details: Optional[Any] = None
timestamp: Optional[str] = None
path: Optional[str] = None
method: Optional[str] = None
class StandardResponse(BaseModel):
"""标准响应格式"""
success: bool
data: Optional[Any] = None
error: Optional[ErrorResponse] = None
timestamp: str
request_id: Optional[str] = None
36.2.2 错误响应构建器
系统提供了错误响应构建器,简化错误响应的创建过程:
# src/utils/response_builder.py
from datetime import datetime
import uuid
from src.schemas.base import ErrorResponse, StandardResponse
class ResponseBuilder:
"""响应构建器"""
@staticmethod
def build_error_response(
code: int,
message: str,
details: Any = None,
path: str = None,
method: str = None
) -> StandardResponse:
"""构建错误响应"""
error = ErrorResponse(
code=code,
message=message,
details=details,
timestamp=datetime.now().isoformat(),
path=path,
method=method
)
return StandardResponse(
success=False,
error=error,
timestamp=datetime.now().isoformat(),
request_id=str(uuid.uuid4())
)
@staticmethod
def build_success_response(data: Any = None) -> StandardResponse:
"""构建成功响应"""
return StandardResponse(
success=True,
data=data,
timestamp=datetime.now().isoformat(),
request_id=str(uuid.uuid4())
)
36.3 HTTP状态码映射机制
36.3.1 错误码与HTTP状态码对应关系
系统建立了完整的错误码与HTTP状态码映射关系,确保错误响应符合RESTful设计原则:
# src/utils/http_status_mapper.py
from enum import IntEnum
class HTTPStatus(IntEnum):
"""HTTP状态码定义"""
OK = 200
CREATED = 201
ACCEPTED = 202
BAD_REQUEST = 400
UNAUTHORIZED = 401
FORBIDDEN = 403
NOT_FOUND = 404
METHOD_NOT_ALLOWED = 405
CONFLICT = 409
UNPROCESSABLE_ENTITY = 422
TOO_MANY_REQUESTS = 429
INTERNAL_SERVER_ERROR = 500
BAD_GATEWAY = 502
SERVICE_UNAVAILABLE = 503
# 错误码到HTTP状态码的映射
ERROR_CODE_TO_HTTP_STATUS = {
# 基础错误码 (1000-1999)
1000: HTTPStatus.BAD_REQUEST, # 参数错误
1001: HTTPStatus.BAD_REQUEST, # 缺少必填参数
1002: HTTPStatus.UNPROCESSABLE_ENTITY, # 参数格式错误
1003: HTTPStatus.BAD_REQUEST, # 参数范围错误
# 业务错误码 (2000-2999)
2000: HTTPStatus.NOT_FOUND, # 草稿不存在
2001: HTTPStatus.CONFLICT, # 草稿已存在
2002: HTTPStatus.NOT_FOUND, # 素材不存在
2003: HTTPStatus.BAD_REQUEST, # 素材格式不支持
2004: HTTPStatus.UNPROCESSABLE_ENTITY, # 素材处理失败
# 系统错误码 (9000-9999)
9000: HTTPStatus.INTERNAL_SERVER_ERROR, # 服务器内部错误
9001: HTTPStatus.BAD_GATEWAY, # 外部服务调用失败
9002: HTTPStatus.SERVICE_UNAVAILABLE, # 服务暂不可用
9003: HTTPStatus.TOO_MANY_REQUESTS, # 请求过于频繁
}
def get_http_status_by_error_code(error_code: int) -> int:
"""根据错误码获取对应的HTTP状态码"""
return ERROR_CODE_TO_HTTP_STATUS.get(error_code, HTTPStatus.INTERNAL_SERVER_ERROR)
36.3.2 状态码映射服务
系统提供了状态码映射服务,自动处理错误码到HTTP状态码的转换:
# src/service/status_code_service.py
from src.utils.http_status_mapper import get_http_status_by_error_code
from src.utils.logger import logger
class StatusCodeService:
"""状态码映射服务"""
@staticmethod
def map_error_to_http_status(error_code: int, error_message: str = None) -> int:
"""将错误码映射为HTTP状态码"""
http_status = get_http_status_by_error_code(error_code)
logger.info(
f"错误码映射: error_code={error_code}, "
f"http_status={http_status}, message={error_message}"
)
return http_status
@staticmethod
def get_status_description(status_code: int) -> str:
"""获取状态码描述"""
descriptions = {
200: "成功",
201: "已创建",
400: "请求错误",
401: "未授权",
403: "禁止访问",
404: "未找到",
422: "无法处理的实体",
500: "服务器内部错误",
502: "网关错误",
503: "服务不可用"
}
return descriptions.get(status_code, "未知状态")
36.4 异常处理中间件实现
36.4.1 全局异常处理中间件
系统通过中间件实现全局异常捕获和处理,确保所有未处理的异常都能被妥善处置:
# src/middlewares/error_handler.py
from fastapi import Request, HTTPException
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
from src.utils.response_builder import ResponseBuilder
from src.service.status_code_service import StatusCodeService
from src.utils.logger import logger
import traceback
class ErrorHandlerMiddleware(BaseHTTPMiddleware):
"""全局错误处理中间件"""
async def dispatch(self, request: Request, call_next):
"""处理请求并捕获异常"""
try:
# 继续处理请求
response = await call_next(request)
return response
except HTTPException as http_exc:
# 处理HTTP异常
logger.warning(
f"HTTP异常: status={http_exc.status_code}, "
f"detail={http_exc.detail}, path={request.url.path}"
)
error_response = ResponseBuilder.build_error_response(
code=http_exc.status_code * 10, # 转换为自定义错误码
message=str(http_exc.detail),
path=str(request.url.path),
method=request.method
)
return JSONResponse(
status_code=http_exc.status_code,
content=error_response.dict()
)
except Exception as exc:
# 处理未预期的异常
logger.error(
f"未预期的异常: {str(exc)}, path={request.url.path}, "
f"method={request.method}", exc_info=True
)
# 记录详细的错误信息
error_details = {
"error_type": type(exc).__name__,
"error_message": str(exc),
"traceback": traceback.format_exc()
}
error_response = ResponseBuilder.build_error_response(
code=9000, # 服务器内部错误
message="服务器内部错误",
details=error_details,
path=str(request.url.path),
method=request.method
)
return JSONResponse(
status_code=500,
content=error_response.dict()
)
36.4.2 自定义异常处理器
系统为不同类型的自定义异常提供专门的处理器:
# src/middlewares/custom_exception_handler.py
from fastapi import Request, HTTPException
from fastapi.responses import JSONResponse
from src.exceptions import CustomException, CustomError
from src.utils.response_builder import ResponseBuilder
from src.service.status_code_service import StatusCodeService
from src.utils.logger import logger
async def custom_exception_handler(request: Request, exc: CustomException) -> JSONResponse:
"""自定义异常处理器"""
# 获取HTTP状态码
http_status = StatusCodeService.map_error_to_http_status(
exc.error_code.value, exc.message
)
logger.warning(
f"自定义异常处理: error_code={exc.error_code.value}, "
f"message={exc.message}, path={request.url.path}"
)
# 构建错误响应
error_response = ResponseBuilder.build_error_response(
code=exc.error_code.value,
message=exc.message,
details=exc.details,
path=str(request.url.path),
method=request.method
)
return JSONResponse(
status_code=http_status,
content=error_response.dict()
)
async def validation_exception_handler(request: Request, exc: Exception) -> JSONResponse:
"""参数验证异常处理器"""
logger.warning(
f"参数验证异常: {str(exc)}, path={request.url.path}, "
f"method={request.method}"
)
# 构建错误响应
error_response = ResponseBuilder.build_error_response(
code=1000, # 参数错误
message="参数验证失败",
details={"validation_errors": str(exc)},
path=str(request.url.path),
method=request.method
)
return JSONResponse(
status_code=400,
content=error_response.dict()
)
36.4.3 异常处理器注册
在FastAPI应用中注册异常处理器:
# src/main.py
from fastapi import FastAPI
from src.middlewares.error_handler import ErrorHandlerMiddleware
from src.middlewares.custom_exception_handler import (
custom_exception_handler,
validation_exception_handler
)
from src.exceptions import CustomException
from pydantic import ValidationError
app = FastAPI(
title="剪映小助手API",
description="视频编辑自动化API服务"
)
# 添加全局错误处理中间件
app.add_middleware(ErrorHandlerMiddleware)
# 注册自定义异常处理器
app.add_exception_handler(CustomException, custom_exception_handler)
app.add_exception_handler(ValidationError, validation_exception_handler)
36.5 错误处理策略
36.5.1 参数验证错误处理
对于参数验证错误,系统提供了详细的错误信息:
# src/validators/param_validator.py
from typing import Any, Dict, List
from src.utils.logger import logger
from src.exceptions import CustomException, CustomError
class ParameterValidator:
"""参数验证器"""
@staticmethod
def validate_required_params(params: Dict[str, Any], required_fields: List[str]) -> None:
"""验证必填参数"""
missing_fields = []
for field in required_fields:
if field not in params or params[field] is None:
missing_fields.append(field)
if missing_fields:
error_message = f"缺少必填参数: {', '.join(missing_fields)}"
logger.warning(f"参数验证失败: {error_message}")
raise CustomException(
error_code=CustomError.MISSING_REQUIRED_PARAMETER,
message=error_message,
details={"missing_fields": missing_fields}
)
@staticmethod
def validate_param_format(param_name: str, param_value: Any, expected_type: type) -> None:
"""验证参数格式"""
if not isinstance(param_value, expected_type):
error_message = f"参数 '{param_name}' 格式错误,期望类型: {expected_type.__name__}"
logger.warning(f"参数格式验证失败: {error_message}")
raise CustomException(
error_code=CustomError.PARAMETER_FORMAT_ERROR,
message=error_message,
details={
"param_name": param_name,
"param_value": param_value,
"expected_type": expected_type.__name__
}
)
36.5.2 资源不存在错误处理
对于资源不存在的场景,系统提供了友好的错误处理:
# src/service/resource_service.py
from src.utils.logger import logger
from src.exceptions import CustomException, CustomError
import os
class ResourceService:
"""资源服务"""
@staticmethod
def validate_draft_exists(draft_path: str) -> None:
"""验证草稿文件是否存在"""
if not os.path.exists(draft_path):
error_message = f"草稿文件不存在: {draft_path}"
logger.warning(f"资源验证失败: {error_message}")
raise CustomException(
error_code=CustomError.DRAFT_NOT_FOUND,
message=error_message,
details={"draft_path": draft_path}
)
@staticmethod
def validate_material_exists(material_url: str) -> None:
"""验证素材是否存在"""
# 这里应该实现素材存在性检查逻辑
# 简化示例
if not material_url or not material_url.startswith("http"):
error_message = f"素材URL无效: {material_url}"
logger.warning(f"素材验证失败: {error_message}")
raise CustomException(
error_code=CustomError.MATERIAL_NOT_FOUND,
message=error_message,
details={"material_url": material_url}
)
36.5.3 系统错误处理
对于系统级错误,系统提供了详细的错误信息记录:
# src/service/system_service.py
import traceback
from src.utils.logger import logger
from src.exceptions import CustomException, CustomError
class SystemService:
"""系统服务"""
@staticmethod
def handle_system_error(error: Exception, context: dict = None) -> None:
"""处理系统错误"""
error_details = {
"error_type": type(error).__name__,
"error_message": str(error),
"traceback": traceback.format_exc(),
"context": context or {}
}
logger.error(
f"系统错误处理: {error_details}",
exc_info=True
)
raise CustomException(
error_code=CustomError.INTERNAL_SERVER_ERROR,
message="服务器内部错误,请稍后重试",
details=error_details
)
36.6 错误信息国际化支持
36.6.1 多语言错误消息管理
系统支持错误消息的国际化,根据用户的语言偏好返回相应的错误信息:
# src/utils/i18n_error_manager.py
from typing import Dict
class I18nErrorManager:
"""国际化错误管理器"""
def __init__(self):
self._messages = {
"zh": {
1000: "参数错误",
1001: "缺少必填参数",
1002: "参数格式错误",
1003: "参数范围错误",
2000: "草稿不存在",
2001: "草稿已存在",
2002: "素材不存在",
2003: "素材格式不支持",
2004: "素材处理失败",
9000: "服务器内部错误",
9001: "外部服务调用失败",
9002: "服务暂不可用",
9003: "请求过于频繁"
},
"en": {
1000: "Parameter error",
1001: "Missing required parameter",
1002: "Parameter format error",
1003: "Parameter range error",
2000: "Draft not found",
2001: "Draft already exists",
2002: "Material not found",
2003: "Material format not supported",
2004: "Material processing failed",
9000: "Internal server error",
9001: "External service call failed",
9002: "Service temporarily unavailable",
9003: "Too many requests"
},
"ja": {
1000: "パラメータエラー",
1001: "必須パラメータが不足しています",
1002: "パラメータフォーマットエラー",
1003: "パラメータ範囲エラー",
2000: "下書きが見つかりません",
2001: "下書きは既に存在します",
2002: "素材が見つかりません",
2003: "素材フォーマットがサポートされていません",
2004: "素材処理に失敗しました",
9000: "サーバー内部エラー",
9001: "外部サービス呼び出しに失敗しました",
9002: "サービスは一時的に利用できません",
9003: "リクエストが多すぎます"
}
}
def get_error_message(self, error_code: int, lang: str = "zh") -> str:
"""获取指定语言的错误消息"""
return self._messages.get(lang, {}).get(error_code, "Unknown error")
def get_localized_error_response(self, error_code: int, lang: str = "zh", details: dict = None) -> dict:
"""获取本地化的错误响应"""
message = self.get_error_message(error_code, lang)
return {
"code": error_code,
"message": message,
"details": details or {},
"language": lang
}
36.6.2 语言检测与选择
系统自动检测用户的语言偏好,选择合适的错误消息语言:
# src/utils/language_detector.py
from typing import Optional
from src.utils.i18n_error_manager import I18nErrorManager
def detect_user_language(request_headers: dict) -> str:
"""从请求头检测用户语言"""
accept_language = request_headers.get("Accept-Language", "zh-CN")
# 简单的语言检测逻辑
if "zh" in accept_language:
return "zh"
elif "en" in accept_language:
return "en"
elif "ja" in accept_language:
return "ja"
else:
return "zh" # 默认中文
def get_localized_error_message(error_code: int, request_headers: dict) -> str:
"""获取本地化的错误消息"""
lang = detect_user_language(request_headers)
i18n_manager = I18nErrorManager()
return i18n_manager.get_error_message(error_code, lang)
36.7 错误日志与监控
36.7.1 结构化错误日志
系统记录结构化的错误日志,便于后续分析和监控:
# src/utils/error_logger.py
from datetime import datetime
from typing import Dict, Any
from src.utils.logger import logger
class ErrorLogger:
"""错误日志记录器"""
@staticmethod
def log_api_error(
error_code: int,
error_message: str,
request_info: Dict[str, Any],
user_info: Dict[str, Any] = None
) -> None:
"""记录API错误日志"""
error_log = {
"timestamp": datetime.now().isoformat(),
"level": "ERROR",
"type": "api_error",
"error_code": error_code,
"error_message": error_message,
"request": {
"method": request_info.get("method"),
"path": request_info.get("path"),
"query_params": request_info.get("query_params"),
"headers": request_info.get("headers"),
"body": request_info.get("body")
},
"user": user_info or {},
"environment": "production"
}
logger.error(f"API错误: {error_log}")
@staticmethod
def log_system_error(
error_type: str,
error_message: str,
stack_trace: str,
context: Dict[str, Any] = None
) -> None:
"""记录系统错误日志"""
error_log = {
"timestamp": datetime.now().isoformat(),
"level": "ERROR",
"type": "system_error",
"error_type": error_type,
"error_message": error_message,
"stack_trace": stack_trace,
"context": context or {},
"environment": "production"
}
logger.error(f"系统错误: {error_log}")
36.7.2 错误统计与告警
系统提供错误统计和告警功能,及时发现和处理问题:
# src/service/error_monitor_service.py
from datetime import datetime, timedelta
from typing import Dict, List
from collections import defaultdict
from src.utils.error_logger import ErrorLogger
class ErrorMonitorService:
"""错误监控服务"""
def __init__(self):
self.error_stats = defaultdict(int)
self.error_timeline = []
self.alert_thresholds = {
"error_rate": 0.1, # 错误率阈值
"error_count": 100, # 错误数量阈值
"time_window": 300 # 时间窗口(秒)
}
def record_error(self, error_code: int, error_message: str, severity: str = "normal") -> None:
"""记录错误信息"""
current_time = datetime.now()
# 更新错误统计
self.error_stats[error_code] += 1
# 记录错误时间线
self.error_timeline.append({
"timestamp": current_time,
"error_code": error_code,
"error_message": error_message,
"severity": severity
})
# 清理过期的错误记录
self._cleanup_old_errors()
# 检查是否需要告警
self._check_alert_conditions()
def _cleanup_old_errors(self) -> None:
"""清理过期的错误记录"""
cutoff_time = datetime.now() - timedelta(seconds=self.alert_thresholds["time_window"])
self.error_timeline = [
error for error in self.error_timeline
if error["timestamp"] > cutoff_time
]
def _check_alert_conditions(self) -> None:
"""检查告警条件"""
if len(self.error_timeline) > self.alert_thresholds["error_count"]:
self._send_alert("错误数量超过阈值")
error_rate = len(self.error_timeline) / max(self.alert_thresholds["time_window"], 1)
if error_rate > self.alert_thresholds["error_rate"]:
self._send_alert("错误率超过阈值")
def _send_alert(self, alert_message: str) -> None:
"""发送告警"""
# 这里应该实现实际的告警发送逻辑
# 可以集成邮件、短信、钉钉等告警方式
print(f"告警: {alert_message}")
print(f"当前错误统计: {dict(self.error_stats)}")
def get_error_statistics(self) -> Dict:
"""获取错误统计信息"""
return {
"error_stats": dict(self.error_stats),
"recent_errors": len(self.error_timeline),
"most_common_errors": self._get_most_common_errors(),
"error_trend": self._calculate_error_trend()
}
def _get_most_common_errors(self) -> List[Dict]:
"""获取最常见的错误"""
sorted_errors = sorted(
self.error_stats.items(),
key=lambda x: x[1],
reverse=True
)
return [{"error_code": code, "count": count} for code, count in sorted_errors[:5]]
def _calculate_error_trend(self) -> str:
"""计算错误趋势"""
if len(self.error_timeline) > 50:
return "high"
elif len(self.error_timeline) > 20:
return "medium"
else:
return "low"
36.8 错误处理最佳实践
36.8.1 异常链传递
在处理复杂业务逻辑时,系统采用异常链传递机制,保留完整的错误上下文:
# src/service/draft_service.py
from src.utils.logger import logger
from src.exceptions import CustomException, CustomError
class DraftService:
"""草稿服务"""
def process_draft(self, draft_url: str) -> dict:
"""处理草稿"""
try:
# 步骤1:验证草稿URL
self._validate_draft_url(draft_url)
# 步骤2:下载草稿文件
draft_content = self._download_draft(draft_url)
# 步骤3:解析草稿内容
parsed_draft = self._parse_draft_content(draft_content)
return parsed_draft
except CustomException as ce:
# 保持原始异常信息,添加上下文
logger.error(f"草稿处理失败: {ce.message}, draft_url={draft_url}")
raise CustomException(
error_code=ce.error_code,
message=f"草稿处理失败: {ce.message}",
details={**ce.details, "draft_url": draft_url}
) from ce
except Exception as e:
# 包装未知异常
logger.error(f"草稿处理时发生未预期错误: {str(e)}, draft_url={draft_url}")
raise CustomException(
error_code=CustomError.INTERNAL_SERVER_ERROR,
message="草稿处理失败",
details={"original_error": str(e), "draft_url": draft_url}
) from e
36.8.2 批量操作错误处理
在处理批量操作时,系统采用部分成功/失败的错误处理策略:
# src/service/batch_service.py
from typing import List, Dict
from src.utils.logger import logger
from src.exceptions import CustomException
class BatchService:
"""批量操作服务"""
def batch_process_drafts(self, draft_urls: List[str]) -> Dict:
"""批量处理草稿"""
results = {
"total": len(draft_urls),
"successful": 0,
"failed": 0,
"success_results": [],
"error_results": []
}
for draft_url in draft_urls:
try:
result = self.process_single_draft(draft_url)
results["success_results"].append({
"draft_url": draft_url,
"result": result
})
results["successful"] += 1
except CustomException as ce:
results["error_results"].append({
"draft_url": draft_url,
"error_code": ce.error_code.value,
"error_message": ce.message,
"details": ce.details
})
results["failed"] += 1
logger.warning(f"批量处理中单个草稿失败: {ce.message}")
except Exception as e:
results["error_results"].append({
"draft_url": draft_url,
"error_code": 9000,
"error_message": str(e),
"details": {}
})
results["failed"] += 1
logger.error(f"批量处理中发生未预期错误: {str(e)}")
return results
36.8.3 错误恢复机制
系统实现了错误恢复机制,在某些情况下可以自动恢复:
# src/utils/error_recovery.py
import time
from typing import Callable, Any
from src.utils.logger import logger
class ErrorRecovery:
"""错误恢复机制"""
@staticmethod
def with_retry(
func: Callable,
max_retries: int = 3,
retry_delay: float = 1.0,
backoff_factor: float = 2.0,
exceptions: tuple = (Exception,)
) -> Any:
"""带重试的错误恢复机制"""
for attempt in range(max_retries + 1):
try:
return func()
except exceptions as e:
if attempt == max_retries:
logger.error(f"重试失败,已达到最大重试次数: {max_retries}")
raise
# 计算延迟时间
delay = retry_delay * (backoff_factor ** attempt)
logger.warning(
f"操作失败,准备重试: attempt={attempt + 1}, "
f"max_retries={max_retries}, delay={delay}s, error={str(e)}"
)
time.sleep(delay)
return None
@staticmethod
def with_circuit_breaker(
func: Callable,
failure_threshold: int = 5,
recovery_timeout: float = 60.0,
expected_exception: type = Exception
) -> Any:
"""熔断器模式错误恢复"""
# 这里应该实现熔断器逻辑
# 简化示例
try:
return func()
except expected_exception as e:
logger.error(f"熔断器触发: {str(e)}")
raise
36.9 错误码文档生成
36.9.1 自动化错误码文档
系统自动生成错误码文档,确保文档与代码保持一致:
# scripts/generate_error_docs.py
from src.exceptions import CustomError
from src.utils.http_status_mapper import ERROR_CODE_TO_HTTP_STATUS
from src.utils.i18n_error_manager import I18nErrorManager
def generate_error_code_documentation():
"""生成错误码文档"""
i18n_manager = I18nErrorManager()
documentation = "# 错误码说明文档\n\n"
documentation += "本文档自动生成,描述了系统中定义的所有错误码及其含义。\n\n"
# 按错误码分类生成文档
error_categories = {
"基础错误 (1000-1999)": [],
"业务错误 (2000-2999)": [],
"系统错误 (9000-9999)": []
}
for error_item in CustomError:
error_code = error_item.value
# 确定错误码分类
if 1000 <= error_code < 2000:
category = "基础错误 (1000-1999)"
elif 2000 <= error_code < 3000:
category = "业务错误 (2000-2999)"
else:
category = "系统错误 (9000-9999)"
# 获取HTTP状态码
http_status = ERROR_CODE_TO_HTTP_STATUS.get(error_code, 500)
# 获取多语言错误消息
zh_message = i18n_manager.get_error_message(error_code, "zh")
en_message = i18n_manager.get_error_message(error_code, "en")
error_info = {
"code": error_code,
"name": error_item.name,
"http_status": http_status,
"zh_message": zh_message,
"en_message": en_message,
"description": f"错误码 {error_code} 的描述信息"
}
error_categories[category].append(error_info)
# 生成文档内容
for category, errors in error_categories.items():
if errors:
documentation += f"## {category}\n\n"
documentation += "| 错误码 | 名称 | HTTP状态码 | 中文消息 | 英文消息 |\n"
documentation += "|--------|------|------------|----------|----------|\n"
for error in errors:
documentation += f"| {error['code']} | {error['name']} | {error['http_status']} | {error['zh_message']} | {error['en_message']} |\n"
documentation += "\n"
return documentation
if __name__ == "__main__":
docs = generate_error_code_documentation()
with open("error_codes.md", "w", encoding="utf-8") as f:
f.write(docs)
print("错误码文档已生成: error_codes.md")
36.10 错误处理测试
36.10.1 错误场景测试用例
系统包含完整的错误场景测试用例,确保错误处理逻辑的正确性:
# tests/test_error_handling.py
import pytest
from fastapi.testclient import TestClient
from src.main import app
from src.exceptions import CustomException, CustomError
client = TestClient(app)
class TestErrorHandling:
"""错误处理测试类"""
def test_parameter_validation_error(self):
"""测试参数验证错误处理"""
response = client.post("/v1/create_draft", json={})
assert response.status_code == 400
response_data = response.json()
assert response_data["success"] is False
assert response_data["error"]["code"] == 1001 # 缺少必填参数
assert "message" in response_data["error"]
def test_resource_not_found_error(self):
"""测试资源不存在错误处理"""
invalid_draft_url = "https://example.com/nonexistent.draft"
response = client.post("/v1/get_draft", json={"draft_url": invalid_draft_url})
assert response.status_code == 404
response_data = response.json()
assert response_data["success"] is False
assert response_data["error"]["code"] == 2000 # 草稿不存在
def test_internal_server_error(self):
"""测试服务器内部错误处理"""
# 这里应该触发一个内部错误
# 简化示例
response = client.post("/v1/gen_video", json={"draft_url": "error_trigger"})
assert response.status_code == 500
response_data = response.json()
assert response_data["success"] is False
assert response_data["error"]["code"] == 9000 # 服务器内部错误
def test_custom_exception_handler(self):
"""测试自定义异常处理器"""
# 测试自定义异常的抛出和处理
with pytest.raises(CustomException) as exc_info:
raise CustomException(
error_code=CustomError.INVALID_PARAMETER,
message="测试自定义异常"
)
assert exc_info.value.error_code == CustomError.INVALID_PARAMETER
assert "测试自定义异常" in str(exc_info.value)
def test_error_response_format(self):
"""测试错误响应格式"""
response = client.post("/v1/create_draft", json={})
response_data = response.json()
# 验证响应格式
assert "success" in response_data
assert "timestamp" in response_data
assert "request_id" in response_data
assert "error" in response_data
# 验证错误信息格式
error_data = response_data["error"]
assert "code" in error_data
assert "message" in error_data
assert "details" in error_data
assert "timestamp" in error_data
assert "path" in error_data
assert "method" in error_data
36.11 本章小结
本章深入剖析了剪映小助手的错误处理机制,从统一错误响应格式设计到HTTP状态码映射,从异常处理中间件实现到具体的错误处理策略,构建了一套完整、健壮的错误处理体系。
通过标准化的错误响应格式,系统确保了所有API接口在发生错误时都能提供一致、清晰的错误信息。HTTP状态码映射机制使得错误响应符合RESTful设计原则,便于客户端理解和处理。全局异常处理中间件实现了统一的异常捕获和处理,避免了异常信息的泄露。
国际化支持使得错误信息能够根据用户的语言偏好进行本地化展示,提升了用户体验。完善的错误日志和监控机制帮助开发团队及时发现和解决问题,确保系统的稳定运行。
这套错误处理机制不仅提高了系统的健壮性和可维护性,也为开发者提供了友好的调试体验,是构建专业API服务的重要组成部分。
附录
代码仓库地址:
- GitHub:
https://github.com/Hommy-master/capcut-mate - Gitee:
https://gitee.com/taohongmin-gitee/capcut-mate
接口文档地址:
- API文档地址:
https://docs.jcaigc.cn