FastAPI:(13)后台任务,元数据,文档URL
由于优快云无法展示「渐构」的「#d,#e,#t,#c,#v,#a」标签,推荐访问我个人网站进行阅读:Hkini
「渐构展示」如下:
#c 概述 文章内容概述
1.后台任务
#d 后台任务
后台任务(Background Task)是FastAPI提供的功能,可以让一些耗时的任务在后端异步执行,而不需要阻塞主线程的请求响应流程。这类任务通常用于处理非即时响应的操作,如发送邮件、数据分析、日志记录、定时任务等。后台任务的执行是非阻塞的,这意味着它们不会影响到用户的请求响应速度。
重要特征:
- 异步执行:后台任务在后台线程中运行,而不阻塞主线程,从而保证了主线程的响应性能。
- 非阻塞性:后台任务的运行不会影响客户端的请求和响应过程。
- 适用于耗时任务:例如邮件发送、数据处理、文件上传等,这些任务通常需要较长的时间来完成,但并不需要立即返回结果。
- 可通过
BackgroundTasks
提供:FastAPI提供的BackgroundTasks
类可以用于启动这些任务。
#e 发送电子邮件(正例) 后台任务
假设一个电子商务平台,在用户下单成功后,系统需要向用户发送订单确认邮件。由于邮件发送是一个网络请求,且可能需要较长时间,所以最好将它放入后台任务中。
特征对比:
- 异步执行:后台任务
send_email
是在主线程外执行的,主线程不会等待邮件发送完成。 - 非阻塞性:请求返回立即响应,邮件发送任务在后台处理,不会阻塞主线程。
- 适用于耗时任务:邮件发送的过程需要一定的时间,适合使用后台任务来处理。
- 通过
BackgroundTasks
提供:通过使用background_tasks.add_task()
启动后台任务。
from fastapi import BackgroundTasks, FastAPI # 导入BackgroundTasks
app = FastAPI()
def write_notification(email: str, message=""): # 创建后台运行的函数,只是一个可以接收参数的「标准函数」
# 由于写入操作不使用 `async` 和 `await`,因此使用普通的 `def` 定义函数
with open("log.txt", mode="w") as email_file:
content = f"notification for {email}: {message}"
email_file.write(content)
@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(write_notification, email, message="some notification")
# 使用 `.add_task()` 方法将「任务函数」传递给「后台任务」对象
return {"message": "Notification sent in the background"}
.add_task()
接收以下参数
- 要在后台运行的任务函数(
write_notification
)。 - 应按顺序传递给任务函数的任何参数序列(
email
)。 - 应传递给任务函数的任何关键字参数(
message="some notification"
)。
#e 记录访问日志(正例) 后台任务
在用户访问某个资源后,为了性能优化,不立即将日志写入磁盘,而是使用后台任务异步地将访问记录写入日志文件中。
特征对比:
- ✅ 异步非阻塞:访问日志在主响应之后记录,不影响资源返回速度。
- ✅ 传参灵活性:路径参数
/resource/
成功传入任务函数。 - ✅ 非实时性任务:日志写入可以延后进行,对主业务没有依赖。
from fastapi import FastAPI, BackgroundTasks
from datetime import datetime
app = FastAPI()
def log_access(path: str):
with open("access.log", "a") as f:
f.write(f"{datetime.now()} - Path Accessed: {path}\n")
@app.get("/resource/")
async def access_resource(background_tasks: BackgroundTasks):
background_tasks.add_task(log_access, "/resource/")
return {"message": "资源访问成功"}
#e 查询数据库并返回(反例) 后台任务
在用户请求中,通过后台任务查询数据库,并想将结果返回给用户。由于后台任务在响应返回之后才执行,这会导致主响应时查询结果尚未完成,用户无法获取所需数据。
特征对比:
- ❌ 异步非阻塞:虽然任务异步执行,但主响应时数据尚未查询完成,用户无法获得期望的查询结果。
- ❌ 非实时性任务误用:此操作本应为实时任务,强依赖查询结果却被放入后台,造成逻辑错误。
- ❌ 自动调度但目的错误:后台任务功能被滥用为主业务的一部分,而非附加操作。
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
user_data = {}
def query_user_data(user_id: str):
# 模拟数据库查询
user_data[user_id] = {"name": "Alice", "age": 30}
@app.get("/get_user/")
async def get_user(user_id: str, background_tasks: BackgroundTasks):
background_tasks.add_task(query_user_data, user_id)
return {"message": "用户数据查询中", "data": user_data.get(user_id)}
#e 依赖注入(正例) 后台任务
使用 BackgroundTasks
也适用于依赖注入系统,可以在多个级别声明类型为 BackgroundTasks
的参数:在「路径操作函数」中,在依赖项(可依赖项)中,在子依赖项中,等等。
FastAPI 知道在每种情况下该做什么以及如何重用同一个对象,以便所有后台任务都合并在一起并在之后在后台运行
在此示例中,「消息将在发送响应后」写入 log.txt
文件。如果请求中存在查询,它将在后台任务中写入日志。然后,在「路径操作函数」中生成的另一个后台任务将使用 email
路径参数写入消息。
from typing import Annotated
from fastapi import BackgroundTasks, Depends, FastAPI
app = FastAPI()
def write_log(message: str):
with open("log.txt", mode="a") as log:
log.write(message)
def get_query(background_tasks: BackgroundTasks, q: str | None = None):
if q:
message = f"found query: {q}\n"
background_tasks.add_task(write_log, message)
return q
@app.post("/send-notification/{email}")
async def send_notification(
email: str, background_tasks: BackgroundTasks, q: Annotated[str, Depends(get_query)]
):
message = f"message to {email}\n"
background_tasks.add_task(write_log, message)
return {"message": "Message sent"}
#c 细节 技术细节
类 BackgroundTasks
直接来自 starlette.background
。
它被直接导入/包含到 FastAPI 中,以便可以从fastapi
中导入它,并避免意外地从starlette.background
中导入备用的BackgroundTask
(末尾没有“s”)。
通过仅使用BackgroundTasks
(而不是BackgroundTask
),就可以将其用作「路径操作函数」参数,并让FastAPI为您处理其余部分,就像直接使用Request
对象一样。
仍然可以在 FastAPI 中单独使用BackgroundTask
,但是必须在代码中创建对象并返回包含它的 Starlette Response
。
可以在Starlette 的背景任务官方文档中查看更多详细信息。
#c 提醒 注意事项
如果需要执行繁重的后台计算,并且不一定需要由同一个进程运行(例如,不需要共享内存、变量等),那么可能受益于使用其他更大的工具,例如Celery。
它们往往需要更复杂的配置,一个消息/作业队列管理器,如 RabbitMQ 或 Redis,但它们允许在多个进程中运行后台任务,尤其是在多个服务器中。
要查看示例,请查看项目生成器,它们都已包含已配置的 Celery。
但是,如果需要访问来自同一个FastAPI应用程序的变量和对象,或者需要执行小的后台任务(例如发送电子邮件通知),可以简单地使用BackgroundTasks
。
2.元数据
#d 元数据
“元数据”是用于补充描述接口信息的辅助数据,不会影响业务逻辑的运行,但会用于 API 文档生成(如 OpenAPI / Swagger UI)以提高可读性、组织性、以及开发者交互体验。元数据常应用于路由分组、接口描述、标签、说明、响应描述等方面。
重要特征:
- 文档增强性:元数据专为 OpenAPI 文档而设计,帮助自动生成更加清晰、结构化的接口文档。
- 业务无关性:元数据不参与业务处理或控制流程,只影响文档呈现。
- 分类组织能力:元数据可以用于给路由分组(如 tags),使接口在 Swagger UI 中分组展示。
- 增强可读性与交互性:通过
summary
、description
等提升接口含义的表达。
参数 | 类型 | 描述 |
---|---|---|
title | str | API 的标题。 |
summary | str | API 的简短摘要。 自 OpenAPI 3.1.0、FastAPI 0.99.0 起可用。 |
description | str | API 的简短描述。它可以使用 Markdown。 |
version | 字符串 | API 的版本。这是你自己的应用程序的版本,而不是 OpenAPI 的版本。例如 2.5.0 。 |
terms_of_service | str | API 的服务条款的 URL。如果提供,则必须为 URL。 |
contact | dict | 公开 API 的联系信息。它可以包含多个字段。 |
license_info | dict | 公开 API 的许可信息。它可以包含多个字段。 |
contact
字段:
参数 | 类型 | 描述 |
---|---|---|
name | str | 联系人/组织的标识名称。 |
url | str | 指向联系信息的 URL。必须为 URL 格式。 |
email | str | 联系人/组织的电子邮件地址。必须为电子邮件地址格式。 |
license_info 字段: |
参数 | 类型 | 描述 |
---|---|---|
name | str | 必需(如果设置了 license_info )。用于 API 的许可证名称。 |
identifier | str | API 的 SPDX 许可证表达式。identifier 字段与 url 字段互斥。 自 OpenAPI 3.1.0、FastAPI 0.99.0 起可用。 |
url | str | API 使用的许可证的 URL。必须为 URL 格式。 |
#e 官网例子(正例) 元数据
可以在 description
字段中写入 Markdown,它将在输出中呈现。
自 OpenAPI 3.1.0 和 FastAPI 0.99.0 起,你也可以使用 identifier
而不是 url
设置 license_info
。
from fastapi import FastAPI
description = """
ChimichangApp API helps you do awesome stuff. 🚀
## Items
You can **read items**.
## Users
You will be able to:
* **Create users** (_not implemented_).
* **Read users** (_not implemented_).
"""
app = FastAPI(
title="ChimichangApp",
description=description,
summary="Deadpool's favorite app. Nuff said.",
version="0.0.1",
terms_of_service="http://example.com/terms/",
contact={
"name": "Deadpoolio the Amazing",
"url": "http://x-force.example.com/contact/",
"email": "dp@x-force.example.com",
},
license_info={
"name": "Apache 2.0",
"identifier": "MIT",
},
)
@app.get("/items/")
async def read_items():
return [{"name": "Katana"}]
#e 标签元数据(正例) 元数据
还可以使用参数 openapi_tags
为用于将路径操作分组的不同标签添加其他元数据。
它接受一个列表,其中包含每个标签的字典。
每个字典可以包含
name
(必需):一个str
,与在「路径操作」和APIRouter
中的tags
参数中使用的相同标签名称。description
:一个str
,包含标签的简短描述。它可以包含 Markdown,并且将显示在文档 UI 中。externalDocs
:一个dict
,描述具有以下内容的外部文档description
:一个str
,包含外部文档的简短描述。url
(必需):一个str
,包含外部文档的 URL。
使用 users
和 items
标签的示例来实现这一点。为标签创建元数据,并将其传递给 openapi_tags
参数
可以在描述中使用 Markdown,例如“login”将显示为粗体(login),“fancy”将显示为斜体(fancy)。
也无需为使用的所有标签添加元数据。
from fastapi import FastAPI
tags_metadata = [ # 创建标签元数据
{
"name": "users",
"description": "Operations with users. The **login** logic is also here.",
},
{
"name": "items",
"description": "Manage items. So _fancy_ they have their own docs.",
"externalDocs": {
"description": "Items external docs",
"url": "https://fastapi.org.cn/",
},
},
]
app = FastAPI(openapi_tags=tags_metadata)
@app.get("/users/", tags=["users"])
async def get_users():
return [{"name": "Harry"}, {"name": "Ron"}]
@app.get("/items/", tags=["items"])
async def get_items():
return [{"name": "wand"}, {"name": "flying broom"}]
3.OpenAPI URl
#c 说明 OpenAPI的URL
默认情况下,OpenAPI 模式位于 /openapi.json
中。但可以使用参数 openapi_url
来配置它。
例如,要将其设置为在 /api/v1/openapi.json
中提供服务。
如果要完全禁用 OpenAPI 模式,可以设置 openapi_url=None
,这也会禁用使用它的文档用户界面。
from fastapi import FastAPI
app = FastAPI(openapi_url="/api/v1/openapi.json")
@app.get("/items/")
async def read_items():
return [{"name": "Foo"}]
#c 说明 文档URL
可以配置包含的两个文档用户界面
- Swagger UI: 在
/docs
中提供服务。- 您可以使用参数
docs_url
设置其 URL。 - 您可以通过设置
docs_url=None
来禁用它。
- 您可以使用参数
- ReDoc: 在
/redoc
中提供服务。- 您可以使用参数
redoc_url
设置其 URL。 - 您可以通过设置
redoc_url=None
来禁用它。
- 您可以使用参数
例如,要将 Swagger UI 设置为在 /documentation
中提供服务并禁用 ReDoc
from fastapi import FastAPI app = FastAPI(docs_url="/documentation", redoc_url=None) @app.get("/items/") async def read_items(): return [{"name": "Foo"}]