首页 > 其他分享 >FastAPI 响应模型指南:从 JSON 数据定义到动态管理的实践

FastAPI 响应模型指南:从 JSON 数据定义到动态管理的实践

时间:2024-12-07 13:57:20浏览次数:9  
标签:指南 name FastAPI id item JSON str model response

FastAPI 响应模型指南:从 JSON 数据定义到动态管理的实践

本篇文章详细介绍了如何在 FastAPI 中使用响应模型,包括在路径操作函数中声明 response_model、处理请求与响应数据不同时的场景,以及通过参数如 response_model_exclude_unset 来优化响应数据。文中还探讨了如何使用 response_model_includeresponse_model_exclude 参数动态控制响应字段的显示,帮助开发者轻松精简响应内容。通过丰富的代码示例和最佳实践建议,您将全面掌握如何定义和管理响应模型,从而确保接口数据的准确性和可维护性,为构建高质量 API 提供坚实基础。

文章目录

在 API 开发中,响应的数据结构直接影响客户端对接口的理解和使用体验。FastAPI 提供了强大的 response_model 功能,通过 Pydantic 模型严格定义接口的响应数据格式,使开发者能够更高效地管理数据结构。以下示例中使用的 Python 版本为 Python 3.10.15,FastAPI 版本为 0.115.4

一 操作函数中声明响应模型

在 FastAPI 中,可以通过使用 response_model 参数来指定路径操作(如 getpost 等方法)的响应模型。

from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list[str] = []


@app.post("/items01/", response_model=Item)
async def create_item(item: Item) -> Any:
    return item


@app.get("/items02/", response_model=list[Item])
async def read_items() -> Any:
    return [
        {"name": "Portal Gun", "price": 42.0},
        {"name": "Plumbus", "price": 32.0},
    ]

不同于 路径操作函数 的其他参数和请求体,response_model 直接作为装饰器方法的参数。此参数接受一个 Pydantic 模型或一个 Pydantic 模型的列表,例如 List[Item] 。这样设置后,响应数据将被限制在指定模型的结构内。运行代码文件 chapter15.py 来启动应用:

$ uvicorn chapter15:app --reload

SwaggerUI 中可以查看在线文档:http://127.0.0.1:8000/docs

二 请求与响应数据相同

class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None
    
@app.post("/user01/")
async def create_user(user: UserIn) -> UserIn:
    return user

定义了一个包含明文密码的 UserIn 模型,用于处理输入和输出数据。这导致在通过浏览器提交密码创建用户时,API 也会在响应中包含密码。虽然 Pydantic 模型推荐复用,但在其他路径操作中使用相同模型可能会导致密码意外泄露给所有客户端。

划重点

永远不要存储或发送用户的明文密码。

三 请求和响应数据不同

class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None

@app.post("/user02/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user

可以创建一个包含明文密码的输入模型 UserIn 和一个不包含密码的输出模型 UserOut。这样,即使路径操作函数返回了包含密码的输入模型,通过将 response_model 设置为 UserOut,FastAPI 将利用 Pydantic 自动过滤掉输出模型中未声明的所有数据。

四 响应数据模型设置默认值

响应模型的数据可以设置默认值。

class Item01(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5
    tags: list[str] = []


items01 = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item01, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items01[item_id]

路径操作装饰器 中设置参数 response_model_exclude_unset=True推荐使用)以排除未显式设置的字段。这有助于确保响应 JSON 中只包含实际设定的字段值。

1 排除默认数据

原始响应 JSON :

{
  "name": "Foo",
  "description": null,
  "price": 50.2,
  "tax": 10.5,
  "tags": []
}

未显式设置的字段在响应 JSON 中不包含:

{
  "name": "Foo",
  "price": 50.2
}
2 默认值字段有实际数据

看 bar 数据:

{
  "name": "Bar",
  "description": "The bartenders",
  "price": 62,
  "tax": 20.2
}
3 默认值与实际数据相同

看 baz 数据:

{
  "name": "Baz",
  "description": null,
  "price": 50.2,
  "tax": 10.5,
  "tags": []
}

在 FastAPI 中,使用 response_model_exclude_unset=True 可有效控制哪些字段包含在响应中,帮助精简响应并避免发送无用或默认数据。此外,还有其他相关参数可以用于细化控制响应数据:

参数描述使用场景
response_model_exclude_unset排除在输出模型中未显式设置的字段。用于防止未显式设置的字段被包含在响应中。
response_model_exclude_defaults排除等于模型中默认值的字段。用于避免包含未明确更改的默认值字段。
response_model_exclude_none排除在输出模型中设为 None 的字段。用于从最终响应中省略明确设置为 None 的字段。

推荐使用response_model_exclude_unset=True,以确保响应中仅包含有意义的数据。

五 include/exclude 管理响应模型

FastAPI允许通过 response_model_includeresponse_model_exclude 参数来管理响应数据中包含或排除的字段。这些参数接收一个属性名称 str 组成的 set ,分别用于指定响应中 应包含的字段(排除其他字段)或 应排除的字段(包含其他字段)。

class Item02(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5


items02 = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
    "baz": {
        "name": "Baz",
        "description": "There goes my baz",
        "price": 50.2,
        "tax": 10.5,
    },
}


@app.get(
    "/items/{item_id}/name",
    response_model=Item02,
    response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
    return items02[item_id]


@app.get("/items/{item_id}/public", response_model=Item02, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
    return items02[item_id]


@app.get(
    "/items/{item_id}/name01",
    response_model=Item02,
    response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
    return items02[item_id]


@app.get("/items/{item_id}/public01", response_model=Item02, response_model_exclude=["tax"])
async def read_item_public_data(item_id: str):
    return items02[item_id]

注:使用 response_model_include response_model_exclude 参数来控制响应字段,生成的OpenAPI 定义和文档中的 JSON Schema 仍将显示完整的数据模型。

推荐使用不同的类来定义不同的响应模型,而不是依赖这些参数,这样做不仅可以清晰地管理数据结构,还可以确保 API 文档的 准确性 和 易用性。

使用列表或元组代替集合
@app.get(
    "/items/{item_id}/name",
    response_model=Item02,
    response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
    return items02[item_id]
  
  
@app.get(
    "/items/{item_id}/name01",
    response_model=Item02,
    response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
    return items02[item_id]

使用 {"name", "description"} 语法可以快速创建一个包含这两个元素的集合(set),这与使用 set(["name", "description"]) 效果相同。如果误用 ["name", "description"] 列表(list)或元组(tuple)代替集合(set),FastAPI 会自动将其转换为集合,并且功能仍然正常运行。

六 完整代码示例

from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list[str] = []


@app.post("/items01/", response_model=Item)
async def create_item(item: Item) -> Any:
    return item


@app.get("/items02/", response_model=list[Item])
async def read_items() -> Any:
    return [
        {"name": "Portal Gun", "price": 42.0},
        {"name": "Plumbus", "price": 32.0},
    ]


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


@app.post("/user01/")
async def create_user(user: UserIn) -> UserIn:
    return user


@app.post("/user02/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user


class Item01(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5
    tags: list[str] = []


items01 = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item01, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items01[item_id]


class Item02(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5


items02 = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
    "baz": {
        "name": "Baz",
        "description": "There goes my baz",
        "price": 50.2,
        "tax": 10.5,
    },
}


@app.get(
    "/items/{item_id}/name",
    response_model=Item02,
    response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
    return items02[item_id]


@app.get("/items/{item_id}/public", response_model=Item02, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
    return items02[item_id]


@app.get(
    "/items/{item_id}/name01",
    response_model=Item02,
    response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
    return items02[item_id]


@app.get("/items/{item_id}/public01", response_model=Item02, response_model_exclude=["tax"])
async def read_item_public_data(item_id: str):
    return items02[item_id]
  

七 源码地址

详情见:GitHub FastApiProj

八 参考

[1] FastAPI 文档

标签:指南,name,FastAPI,id,item,JSON,str,model,response
From: https://blog.csdn.net/u014394049/article/details/144187554

相关文章

  • 【转载】Jsonpath
    原文:史上最详细的jsonpath教程,它来了!!!https://www.jianshu.com/p/3f5b9cc88bde例子:{"store":{"book":[{"category":"reference","author":"NigelRees","title":&quo......
  • 【springboot编程】Spring Boot注解全面解析与实战指南
    SpringBoot的注解体系极大简化了Java应用程序的开发,减少了大量的手动配置和重复代码。通过使用注解,开发者能够快速构建具有复杂功能的应用程序,同时保持代码的简洁和可维护性。无论是异常处理、自动配置,还是依赖注入,这些注解都为开发者提供了灵活而强大的工具,使应用程序的开发......
  • 深入浅出ArkTS开发指南之调用接口将 json 对象渲染到界面
    大家好,我是全栈的峰哥,独立开发者,相关问题可以加v:2588234,联系我一起沟通今天主要给大家带来通过调用接口,通过返回参数转换成我们需要的对象,并通过状态管理,渲染到界面上,主要案例是在首页需要调用轮播图接口list,并在首页上能展示轮播图定义实体首选,我们需要先定义接口返回的json......
  • 云服务器上的域名解析与备案全流程指南
    在部署网站时,云服务器与域名是两个不可缺少的重要元素。为了让用户能够通过域名访问你的网站,你需要完成域名解析和网站备案的流程。域名解析将域名与云服务器的IP地址相对应,而备案是中国大陆地区网站上线前的必备流程。本文将详细介绍在云服务器上进行域名解析和备案......
  • 利用市场分析工具对特定国家的产品市场情况进行深入分析的全面指南
    市场分析是产品进入新市场前必须进行的重要步骤。要理解一个产品在某个国家的市场情况,我们需要利用各种市场分析工具来收集并分析数据,包括市场趋势、消费者需求、竞争对手等。通过有效利用这些工具,企业能够找到最佳市场进入策略,避免潜在风险。本文将介绍一系列常用的市场分......
  • 探索Volc Engine MaaS:快速入门指南
    探索VolcEngineMaaS:快速入门指南VolcEngine的MaaS(ModelasaService)是一种强大的服务,允许开发者使用其LLM(大语言模型)实现多种自然语言处理任务。在这篇文章中,我们将指导您如何开始使用VolcEngine的MaaS模型,通过简单的步骤和代码示例展示其应用。1.引言VolcEngine......
  • GoTrackIt应用指南:共享单车时空轨迹可视化
    GoTrackIt平台集成了对Kepler.gl可视化工具的部分功能进行了封装,通过引入KeplerVis类,显著简化了地理空间数据分析与展示的过程。利用这一类,开发者和数据分析师能够在网页端快速实现复杂地理数据的动态可视化,而无需深入掌握Kepler.gl的底层细节。KeplerVis提供了一系列......
  • [深入探索Brave Search API:使用实例和应用指南]
    #深入探索BraveSearchAPI:使用实例和应用指南##引言随着隐私保护和数据收集问题日益受到关注,越来越多的用户和开发者开始寻找替代传统搜索引擎的解决方案。BraveSearch作为一款新兴搜索引擎,凭借其独立的网络索引和隐私友好的设计,吸引了很多人的关注。本文将深入探讨......
  • Python模块之random、hashlib、json、time等内置模块语法学习
    Python内置模块语法学习random、hashlib、json、time、datetime、os等内置模块语法学习模块简单理解为就是一个.py后缀的一个文件分为三种:内置模块:python自带,可调用第三方模块:别人设计的,可调用自定义模块:自己编写的,可调用模块之间苦于相互调用,是Python最高级别的组织......
  • json学习
    JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式,易于人阅读和编写,也易于机器解析和生成。它通常用于在服务器和客户端之间交换数据,特别是在Web应用中。JSON格式基于JavaScript对象表示法,但它是语言独立的,广泛用于多种编程语言中。JSON使用文本格式来存储和表......