前置知識
JSONResponse:https://www.cnblogs.com/poloyy/p/15364445.html
response_model:https://www.cnblogs.com/poloyy/p/15317585.html
背景
在寫辣雞平台,然后有統一的自定義 JSONResponse,所以全部路徑函數都是返回自定義 JSONResponse 的,比如
@router.post("/save", response_model=UserResponse) async def save(user_save: UserSave, db: Session = Depends(get_db)) -> JSONResponse: ... return SuccessResponse(message="123", data=123) @router.post("/user_list", response_model=UserListResponse) async def get_user_list( user_page: UserPage, db: Session = Depends(get_db) ) -> JSONResponse: ... return SuccessResponse(data=123) @router.put("/active", response_model=BaseResponse) async def active( id: int = Body(..., description="用戶ID"), state: States = Body(default=States.ACTIVE, description="用戶狀態"), db: Session = Depends(get_db), ) -> JSONResponse: ... return SuccessResponse(message="123", data=123)
- 這里的 SuccessResponse 就是繼承 JSONResponse,是一個自定義響應對象
- 然后也可以看到三個路徑函數都指定了 response_model
問題來了
路由操作函數返回的是自定義 JSONResponse,同時指定了 response_model,按道理最后返回的響應數據應該被限制為 model 里面的數據才對,但實際並沒有
為啥我會發現這個問題呢
- 在我創建 user 之后,想返回整個 user 對象給前端,但自然而然 password 是不可以返回的,所以 response_model 就去掉這個 password 字段
- 但最終返回的響應體仍然有 password 字段
重新演示一遍上述問題現象
fastapi 代碼
from fastapi import FastAPI import uvicorn app = FastAPI() class UserBase(BaseModel): username: str email: str class UserCreate(UserBase): password: str fake_db = [] # response_model 的 UserBase 只包含 username、email 沒有 password @app.post("/create", response_model=UserBase) async def create(user: UserCreate): # 模擬:添加數據進數據庫 fake_db.append(user) # 模擬拿到完整的 user user = jsonable_encoder(user) return JSONResponse(status_code=200, content=user) if __name__ == "__main__": uvicorn.run("test:app", port=8001, debug=True)
查看 OpenAPI 文檔
因為添加了 response_model,所以 Responses 的 Example Value 是按照 response_model 的數據來生成的,就沒有 password
發起請求,查看響應
但真實發送請求后,還是有 password 字段
return 字典代替 JSONResponse
@app.post("/create", response_model=UserBase) async def create(user: UserCreate): # 模擬:添加數據進數據庫 fake_db.append(user) # 模擬拿到完整的 user user = jsonable_encoder(user) return user
再發起請求,查看響應
假設最終 return 的是一個字典,那么 response_model 就可以限制它的響應數據了,所以這里沒有 password
根本原因
首先要記得 response_model 的作用
- 將輸出數據轉換為 Model 中聲明的類型
- 驗證數據
- 在 OpenAPI 給 Response 添加 JSON Schema 和 Example Value
- 最重要:將輸出數據限制為 model 的數據
再來,在 return 那打個斷點,Debug 分別看看兩種 return 的場景
return user 斷點
- 斷點后 F7 進入的就是這里
- 在經過 fastapi 內部一長串各種調用處理后,response 本身包含 password 字段,但最后得到的 response_data 已經去掉了 password 字段
- 得到這個 response_data 后,最后還是會將它賦值給 JSONResponse 的 content ,然后接口再返回給前端
return JSONResponse 斷點
- 斷點后 F7 進入的就是這里
- 和上面完全不一樣,跳過了前面 fastapi 處理數據的一長串步驟
- 因為這里是直接 return JSONResponse,所以 content 值已經確定了
- 最后賦什么值,接口返回的就是什么,並不會受 response_model 的限制
那 return JSONResponse 還有必要設置 response_model 嗎?
- 如果是合作開發,還是有必要的,因為 response_model 可以自動添加 JSON Schema、Example Value 在 Swagger 文檔中,可讀性大大提升
- 但它的作用也僅是提供 JSON Schema、Example Value