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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM