zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    IntelliJ IDEA快捷键
    Find Minimum in Rotated Sorted Array
    爬取淘宝交易记录的爬虫
    MR并行算法编程过程中遇到问题的思考
    Map.Entry用法示例
    给定一组数和一个目标值,返回和为目标值的集合(集合中的元素可重复)
    位运算:获取集合的子集
    Linux每次开机都要source profile的解决办法
    mysql数据导入导出
    linux下nginx编译安装(抄别人的,方便查看)
  • 原文地址:https://www.cnblogs.com/poloyy/p/15548909.html
Copyright © 2011-2022 走看看