Appearance
47|全局异常处理与错误响应
接口运行过程中不可避免地会出错:数据库连接断开、参数校验失败、业务规则冲突。没有统一处理的异常会直接把 Python 堆栈返回给客户端,既暴露内部实现,又让用户看不懂。FastAPI 提供异常处理器(exception handler),把不同类型的异常转换为统一的错误响应格式。
一、HTTPException
FastAPI 内置的异常,用于主动返回 HTTP 错误:
python
from fastapi import HTTPException, status
@app.get("/servers/{id}")
async def get_server(id: int):
server = find_server(id)
if not server:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="服务器不存在",
)
return server| 参数 | 作用 |
|---|---|
status_code | HTTP 状态码 |
detail | 错误描述,返回给客户端 |
headers | 额外的响应头(如 401 时的 WWW-Authenticate) |
HTTPException 会被 FastAPI 自动捕获并转成 JSON 响应:
json
{"detail": "服务器不存在"}二、自定义异常
业务逻辑复杂时,用自定义异常更清晰:
python
class BusinessError(Exception):
def __init__(self, code: int, message: str):
self.code = code
self.message = message
class ResourceNotFound(BusinessError):
def __init__(self, resource: str):
super().__init__(code=1001, message=f"{resource} 不存在")
class PermissionDenied(BusinessError):
def __init__(self):
super().__init__(code=1002, message="权限不足")服务层抛出业务异常:
python
def get_server(server_id: int) -> Server:
server = db.get(Server, server_id)
if not server:
raise ResourceNotFound("服务器")
return server三、全局异常处理器
python
from fastapi import Request
from fastapi.responses import JSONResponse
@app.exception_handler(BusinessError)
async def business_error_handler(request: Request, exc: BusinessError):
return JSONResponse(
status_code=400,
content={"code": exc.code, "message": exc.message, "data": None},
)
@app.exception_handler(HTTPException)
async def http_error_handler(request: Request, exc: HTTPException):
return JSONResponse(
status_code=exc.status_code,
content={"code": exc.status_code, "message": exc.detail, "data": None},
headers=exc.headers,
)
@app.exception_handler(Exception)
async def generic_error_handler(request: Request, exc: Exception):
logger.exception("未处理的异常")
return JSONResponse(
status_code=500,
content={"code": 500, "message": "服务器内部错误", "data": None},
)| 处理器 | 捕获范围 |
|---|---|
BusinessError | 自定义业务异常 |
HTTPException | FastAPI 内置 HTTP 异常 |
Exception | 所有其他未捕获的异常(最后一道防线) |
四、统一错误响应格式
python
class ErrorResponse(BaseModel):
code: int
message: str
data: None = None所有错误都按这个格式返回,前端只需要一套错误处理逻辑:
js
async function request(url, options) {
const response = await fetch(url, options);
const data = await response.json();
if (data.code !== 0) {
throw new Error(data.message);
}
return data.data;
}五、Pydantic 校验错误定制
Pydantic 校验失败默认返回 422,格式是 FastAPI 固定的。可以定制为统一格式:
python
from fastapi.exceptions import RequestValidationError
@app.exception_handler(RequestValidationError)
async def validation_error_handler(request: Request, exc: RequestValidationError):
errors = exc.errors()
message = "; ".join([f"{'.'.join(map(str, e['loc']))}: {e['msg']}" for e in errors])
return JSONResponse(
status_code=422,
content={"code": 422, "message": message, "data": None},
)六、隐藏生产环境堆栈
python
import os
ENV = os.getenv("ENV", "development")
@app.exception_handler(Exception)
async def generic_error_handler(request: Request, exc: Exception):
logger.exception("未处理的异常")
content = {"code": 500, "message": "服务器内部错误", "data": None}
if ENV == "development":
content["detail"] = str(exc) # 开发环境返回详细错误
return JSONResponse(status_code=500, content=content)生产环境只返回"服务器内部错误",不暴露堆栈;开发环境可以返回详细错误方便调试。
七、常见错误
异常处理器中再次抛异常
python
@app.exception_handler(Exception)
async def bad_handler(request, exc):
raise AnotherException() # 错误:处理器抛异常会导致服务崩溃404 路由用 400 返回
python
# 错误:资源不存在用 400(Bad Request 是请求语法错误)
raise HTTPException(status_code=400, detail="服务器不存在")
# 正确
raise HTTPException(status_code=404, detail="服务器不存在")忘记处理 Pydantic 校验错误
Pydantic 校验失败默认返回的格式和自定义的统一格式不一致,需要专门注册 RequestValidationError 处理器。