FastAPI 错误处理模式指南
FastAPI 错误处理实战:HTTPException 的正确用法、全局异常处理器、自定义异常体系与错误日志集中管理,构建健壮 API 的完整方案。
直接回答:FastAPI 的错误处理分三层:路由内用 HTTPException 返回精确的状态码与消息;全局 exception_handler 兜住漏网异常;自定义异常类体系化业务错误。 同时记录结构化日志,便于按请求查找异常。
认识 FastAPI 的错误
FastAPI 里的错误都是 Python Exception 家族的成员,同样分三类:运营性错误(预期内的外部因素:资源不存在、参数非法)、代码缺陷(Bug,应告警修复)、外部服务故障(需重试与降级)。区分它们,才能决定"返回什么状态码、要不要叫醒值班人"。
HTTPException:路由内的精确表达
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/users/{uid}")
def get_user(uid: int):
user = db.get(uid)
if user is None:
raise HTTPException(status_code=404, detail="用户不存在")
return user
HTTPException 自带状态码与 detail,FastAPI 自动序列化为 JSON 响应。校验类错误(请求体不合法)由 Pydantic 自动返回 422,无需手写。
全局异常处理器
import logging
from fastapi import Request
logger = logging.getLogger(__name__)
from fastapi.responses import JSONResponse
@app.exception_handler(Exception)
async def unhandled(request: Request, exc: Exception):
logger.exception("未捕获异常: %s %s", request.method, request.url.path)
return JSONResponse(status_code=500, content={"detail": "服务器开小差了"})
针对特定异常注册处理器,并设置 debug=False;已经发出的流式响应和后台任务失败不能再改写客户端状态。内置的 RequestValidationError 也可以覆写,把 422 的错误格式改造成前端爱吃的形状。
自定义异常体系
class AppError(Exception):
status_code = 400
code = "app_error"
def __init__(self, detail: str):
self.detail = detail
class InsufficientBalance(AppError):
code = "insufficient_balance"
@app.exception_handler(AppError)
async def app_error_handler(request: Request, exc: AppError):
return JSONResponse(
status_code=exc.status_code,
content={"code": exc.code, "detail": exc.detail},
)
业务代码里 raise InsufficientBalance("余额不足"),响应结构统一带 code——前端按 code 做国际化与分支处理,比解析文案靠谱得多。
错误日志的正确姿势
- 预期内的 4xx:INFO/WARNING 级,只记录必要且脱敏的用户标识与参数,不用堆栈;
- 5xx 与未捕获异常:ERROR 级 + 完整堆栈,
logger.exception是标准写法; - 结构化输出(JSON):时间、级别、路径、trace_id 字段化,机器可读才能聚合告警。
日志集中化
多副本应用应集中采集各实例的日志,并让错误响应与日志记录使用同一个 request_id。用户报障时,先用这个标识找对应的异常堆栈。送入观测云时,可按 DataKit 日志采集指南配置采集,再用一条测试错误核对路径、级别和请求标识是否保留;request_id 用于定位日志,不会自动生成分布式调用链。
常见问题(FAQ)
Q:HTTPException 和自定义异常怎么分工?
A:HTTP 语义明确的(404/401/403)直接 HTTPException;业务语义强的(余额不足、库存不够)走自定义异常体系,错误码更稳定。
Q:后台任务里的异常怎么处理?
A:不会传给客户端,必须任务内捕获并记日志(最好落任务状态表)。静默失败是后台任务的头号杀手。
Q:要在响应里暴露内部错误细节吗?
A:绝不。对外统一"服务器内部错误" + request_id;细节进日志。堆栈、SQL、内部主机名都是攻击者的情报。
官方参考
本文基于官方文档整理,未进行运行时或性能测试。示例中的业务函数、数据模型和部署地址需结合项目补全;局部片段不等同于完整生产应用。