fastapi(65)- 路由函數指定了 response_model,在返回自定義 JSONResponse 時, 不會限制它返回的數據結構

前置知識

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章