FastAPI 错误处理
FastAPI 错误处理学习笔记
一、HTTPException — 主动抛出错误
1. 基本用法
fromfastapiimportFastAPI,HTTPException app=FastAPI()items={"foo":"The Foo Wrestlers"}@app.get("/items/{item_id}")asyncdefread_item(item_id:str):ifitem_idnotinitems:raiseHTTPException(status_code=404,detail="Item not found")return{"item":items[item_id]}关键点:
HTTPException是异常类,必须用raise抛出,不是return。- FastAPI 捕获后会将其转换为 HTTP 响应,返回对应的 status_code 和 JSON body。
- 返回格式:
{"detail": "Item not found"}
2. 自定义 detail
detail可以是任何 JSON 可序列化的值:
raiseHTTPException(status_code=404,detail={"msg":"Item not found","item_id":item_id})响应:
{"detail":{"msg":"Item not found","item_id":"bar"}}3. 添加自定义响应头
raiseHTTPException(status_code=401,detail="Not authenticated",headers={"WWW-Authenticate":"Bearer"},)典型场景:401 响应需要返回
WWW-Authenticate头,告知客户端应使用何种认证方式。
二、自定义异常处理器
1. 注册异常处理器 —@app.exception_handler
fromfastapiimportFastAPI,Requestfromfastapi.responsesimportJSONResponseclassUnicornException(Exception):def__init__(self,name:str):self.name=name app=FastAPI()@app.exception_handler(UnicornException)asyncdefunicorn_exception_handler(request:Request,exc:UnicornException):returnJSONResponse(status_code=418,content={"message":f"Oops!{exc.name}did something."},)@app.get("/unicorns/{name}")asyncdefread_unicorn(name:str):ifname=="yolo":raiseUnicornException(name=name)return{"unicorn_name":name}流程:
- 业务代码
raise UnicornException - FastAPI 捕获后调用注册的
unicorn_exception_handler - 处理器返回自定义的
JSONResponse
2. 处理器签名
asyncdefhandler(request:Request,exc:SomeException)->Response:...| 参数 | 类型 | 说明 |
|---|---|---|
request | Request | 当前请求对象 |
exc | 异常类实例 | 被抛出的异常对象 |
三、覆盖默认异常处理器
FastAPI 内置两个默认处理器,可以被覆盖:
1. RequestValidationError 处理器
默认行为:返回 422 + 详细校验错误信息。
fromfastapi.exceptionsimportRequestValidationErrorfromfastapi.responsesimportPlainTextResponse@app.exception_handler(RequestValidationError)asyncdefvalidation_exception_handler(request,exc):returnPlainTextResponse(str(exc),status_code=400)2. HTTPException 处理器
fromstarlette.exceptionsimportHTTPExceptionasStarletteHTTPException@app.exception_handler(StarletteHTTPException)asyncdefhttp_exception_handler(request,exc):returnPlainTextResponse(f"Oops!{exc.detail}",status_code=exc.status_code)注意:覆盖
HTTPException处理器时,应导入starlette.exceptions.HTTPException,而非fastapi.HTTPException。FastAPI 的HTTPException继承自 Starlette 的版本,但增加了headers等额外功能。
四、异常处理优先级
请求进入 │ ├─ 业务代码 raise CustomException → 匹配 @app.exception_handler(CustomException) │ ├─ 业务代码 raise HTTPException → 匹配 @app.exception_handler(HTTPException) │ (未覆盖则走默认处理器) │ ├─ 请求校验失败 → RequestValidationError → 匹配对应处理器 │ └─ 未捕获异常 → 500 Internal Server Error(默认处理器)匹配规则:按异常类的继承链,从具体到通用匹配。子类异常优先匹配子类处理器。
五、统一响应格式实践
实际项目中,通常需要统一的错误响应格式:
fromfastapiimportFastAPI,Request,HTTPExceptionfromfastapi.exceptionsimportRequestValidationErrorfromfastapi.responsesimportJSONResponsefrompydanticimportBaseModel app=FastAPI()# ---- 统一响应模型 ----classErrorResponse(BaseModel):code:intmessage:strdetail:object=None# ---- 统一错误返回函数 ----deferror_response(code:int,message:str,detail=None,status_code:int=None):returnJSONResponse(status_code=status_codeorcode,content=ErrorResponse(code=code,message=message,detail=detail).model_dump(),)# ---- HTTPException 处理器 ----@app.exception_handler(HTTPException)asyncdefhttp_exception_handler(request:Request,exc:HTTPException):returnerror_response(code=exc.status_code,message=exc.detailifisinstance(exc.detail,str)elsestr(exc.detail),status_code=exc.status_code,)# ---- 校验错误处理器 ----@app.exception_handler(RequestValidationError)asyncdefvalidation_exception_handler(request:Request,exc:RequestValidationError):returnerror_response(code=422,message="请求参数校验失败",detail=exc.errors(),status_code=422,)# ---- 兜底:未知异常 ----@app.exception_handler(Exception)asyncdefgeneric_exception_handler(request:Request,exc:Exception):returnerror_response(code=500,message="服务器内部错误",status_code=500,)响应示例
{"code":422,"message":"请求参数校验失败","detail":[{"loc":["query","page"],"msg":"value is not a valid integer","type":"type_error.integer"}]}六、HTTPException vs 自定义异常
| 对比项 | HTTPException | 自定义异常 +exception_handler |
|---|---|---|
| 使用方式 | raise HTTPException(...) | raise MyException(...)+ 注册处理器 |
| 适用场景 | 简单的 HTTP 错误返回 | 需要自定义逻辑/格式的错误处理 |
| 响应控制 | 直接指定 status_code | 在处理器中灵活控制 |
| 可复用性 | 通用 | 可封装业务语义 |
| 是否中断代码 | 是(raise) | 是(raise) |
七、注意事项
raise而非return:HTTPException是 Python 异常,必须raise,否则不会触发错误处理流程。- 不要在处理器中再抛异常:异常处理器中如果再次
raise,可能导致无限循环或 500 错误。 - 生产环境隐藏细节:
RequestValidationError默认返回详细的字段校验信息,生产环境建议精简,避免泄露接口结构。 - 日志记录:在异常处理器中添加日志,便于排查问题:
importlogging logger=logging.getLogger(__name__)@app.exception_handler(Exception)asyncdefgeneric_exception_handler(request:Request,exc:Exception):logger.exception(f"Unhandled exception:{exc}")returnerror_response(code=500,message="服务器内部错误",status_code=500)- 异步/同步均可:
exception_handler支持同步和异步函数,但推荐使用async def。
