type
Post
status
Published
date
Mar 13, 2026
slug
fastapi001
summary
tags
python
FastAPI
学习笔记
category
python
icon
password
example-row

01.async/await

在 Python 中,async def 用于定义协程函数(coroutine function)。调用协程函数会返回一个协程对象,而非立即执行函数体。协程函数的执行需要通过事件循环(event loop)调度,或通过 await 关键字等待其完成。

无 await 的影响

1. 执行方式:同步执行
当协程函数内部没有使用 await 时,函数体将同步执行,不会让出控制权给事件循环。这意味着:
  • 函数会像普通函数一样从头到尾执行。
  • 不会触发异步调度机制。
  • 即使函数体包含耗时操作(如 IO),也会阻塞事件循环。
2. 性能影响:失去异步优势
异步编程的核心优势是在等待 IO 操作时让出控制权,让事件循环可以处理其他任务。如果协程函数内部没有 await,则:
  • 无法利用事件循环的并发处理能力。
  • 耗时操作会阻塞整个应用。
  • 与普通同步函数相比,没有性能提升。
3. 语法与运行:不会报错
重要的是,没有 await 的协程函数不会导致语法错误,依然可以正常运行。但会失去异步编程的意义。

在 FastAPI 中的影响

  1. 路由函数标记为异步:FastAPI 会使用事件循环调度此函数
  1. 无 await 的后果
      • 函数体同步执行(因为没有挂起点)。
      • 如果函数体包含耗时操作(如数据库查询、网络请求),会阻塞事件循环。
      • 影响 API 的并发处理能力。

何时需要 await

在 FastAPI 路由函数中,以下情况需要使用 await
  • 调用其他异步函数(如 await db.query(...)
  • 执行 IO 操作(如文件读写、网络请求)
  • 调用异步库的方法(如异步数据库驱动)
因此,是否需要 await 取决于函数内部是否有需要异步处理的操作。对于简单的参数处理和返回,无 await 是完全可以的。
 

02.接口顺序

由于路径操作是按顺序依次运行的,因此,一定要在 /users/{user_id} 之前声明 /users/me ,否则/users/me 将把me作为 /users/{user_id}的路径参数,而不会调用到 /users/me 接口。
 

03. 接口中包含路径的路径参数

假设路径操作的路径为 /files/{file_path}。但需要 file_path 中也包含路径,比如,home/johndoe/myfile.txt
此时,该文件的 URL 是这样的:/files/home/johndoe/myfile.txt
OpenAPI 不支持声明包含路径的路径参数,因为这会导致测试和定义更加困难。
不过,仍可使用 Starlette 内置工具在 FastAPI 中实现这一功能。而且不影响文档正常运行,但是不会添加该参数包含路径的说明。
直接使用 Starlette 的选项声明包含路径的路径参数:
本例中,参数名为 file_path,结尾部分的 :path 说明该参数应匹配路径。
如果 file_ path值为 /home/johndoe/myfile.txt ,请求的url则为` URL 是 /files//home/johndoe/myfile.txt
 

04.接口可选参数与必选参数

  • 将查询参数默认值设为 None 即可声明可选的查询参数。
  • 查询参数不声明默认值,即为必选查询参数。
  • 如果为不是路径参数的参数声明了默认值,该参数也是可选的。
  • FastAPI 可以识别同时声明的多个路径参数和查询参数。而且声明查询参数的顺序并不重要。因为FastAPI 通过参数名进行检测。
本例中有 3 个查询参数:
  • needy,必选的 str 类型参数。
  • skip,默认值为 0 的 int 类型参数。可选。
  • limit,可选的 int 类型参数。
 

05. 接口查询参数转换

访问:
http://127.0.0.1:8000/items/foo?short=1
http://127.0.0.1:8000/items/foo?short=True
http://127.0.0.1:8000/items/foo?short=true
http://127.0.0.1:8000/items/foo?short=on
http://127.0.0.1:8000/items/foo?short=yes
或其它任意大小写形式(大写、首字母大写等),函数接收的 short 参数都是布尔值 True。否则为 False
 

06.请求体

函数参数按如下规则进行识别:
  • 如果该参数也在路径中声明了,它就是路径参数。
  • 如果该参数是(intfloatstrbool 等)单一类型,它会被当作查询参数
  • 如果该参数的类型声明为 Pydantic 模型,它会被当作请求体
注意
FastAPI 强制规则
  • POST/PUT/PATCHPydantic模型 → 自动解析为 请求体 (Request Body)。
  • GET/DELETEGET /DELETE请求没有请求体,所有数据只能放在 URL 里。不允许 直接Pydantic模型 接收参数,因为请求时并不会自动转换为URL参数,请求时会直接报错。
  • 如果GET/DELETE请求一定要用Pydantic模型 接收参数,需要配合Query使用。
  • 类似GET请求用Pydantic模型 接收参数,需要配合Query使用。POST/PUT/PATCH 请求如果要将单一类型参数通过json body传递,而不是查询参数,可以使用Body
 

07.Annotated 用法

函数的查询参数 q: str | None = None 语义为q 是一个可以是 str 或 None 的参数,默认是 None。使用Annotated 方式实现如下:
  1. 结合Query实现多个条件校验。
2.在使用 Query 的同时需要把某个值声明为必填时,只需不声明默认值。下面示例为允许为None,但必填。
下面示例为不允许为None,但必填。
 

08.查询参数列表

访问http://localhost:8000/items/?q=foo&q=bar ,URL 的响应将会是:
注意:
  • 要声明类型为 list 的查询参数,要显式地使用 Query,否则它会被解释为请求体。
  • list[str] 改为list,如q: Annotated[list, Query()] = [] ,FastAPI 会由之前检查列表内容必须为为string变成不会检查列表的内容的类型。
 

09.别名参数

请求示例如下:
注意,因为使用了别名,原始参数名 q 不会被自动识别,即不再可以使用q进行有效传参了。
 

10.文档隐藏参数

声明查询参数,但在自动生成的api文档上不展示该查询参数。通过将 Query 的参数 include_in_schema 设为 False 实现。
 

11.路径参数校验

从 fastapi 导入 Path 进行校验,用法类似查询参数使用Query进行校验。
 

12.参数顺序问题

  • Python 函数定义规则:位置参数(无默认值)必须在关键字参数(有默认值)之前。
  • FastAPI 的 Path 函数:即使没有显式设置默认值,Path(...) 仍被视为有默认值的参数。
  • 使用 Annotated,不存在触发python语法错误的问题,因为没有使用 Query() 或 Path() 作为函数参数的默认值。
  • 在函数的第一个参数位置传入 * ,后面所有参数都应该作为关键字参数(键值对)来调用,也被称为 kwargs。即使它们没有默认值,这样也能规避顺序问题。

13.Field使用示例

Field用法上与QueryPath 、Body 相同。
注意:
通常情况下,如果函数参数是一个 Pydantic 模型(如 item: Item),FastAPI 期望接收到的 JSON 格式是直接的字段:
这里Body(embed=True) 设置了 embed=True 时,FastAPI 期望请求体中包含一个以参数名(这里是 item)为键的 JSON 对象:
如果函数参数存在多个Pydantic 模型,无论是否设置了embed属性,都按embed=True 处理。
期望传参:

14.使用dict传请求体

将请求体声明为使用某类型的键和其他类型值的 dict ,可以用于接收一些尚且未知的键值对。
 JSON 仅支持将 str 作为键,但是 Pydantic 具有自动转换数据的功能,API 客户端只能将字符串作为键发送,Pydantic 就会对其进行转换并校验字符串内容是否仅包含整数。

15.header命名问题

HTTP 标准请求头用连字符 -,但在 FastAPI 代码里,必须写成下划线 _,因为 Python 语法不允许变量名用连字符 - 。FastAPI 在定义Header参数时,会自动把下划线 _ 转换成连字符 - 去匹配请求头。
极少数情况下,请求头本身就包含下划线(不标准,但可能遇到),可以用convert_underscores=False 禁用转换:
获取请求头:
  • 单字段模式Annotated[类型, Header()]告诉 FastAPI:"这个参数的值来自请求头中的同名参数"。
  • 模型模式Annotated[模型类, Header()]告诉 FastAPI:"创建一个这个模型的实例,用请求头中的数据填充"。
 

16.response_model指定返回类型

  1. 因为两个模型不同,如果我们将函数返回类型定为 UserOut,编辑器和工具会报返回了无效类型,因为它们是不同的类。而使用response_model可以自动转换指定的模型UserOut
  1. -> Any指定任意类型,等效于不写返回类型,FastAPI会放弃对返回类型的校验。
  1. FastAPI 接口返回只有 2 种情况:
      • 返回普通数据(dict、Pydantic 模型):FastAPI 自动转 JSON。
      • 返回 Response 对象(RedirectResponse、JSONResponse、HTMLResponse):直接返回给浏览器,不做处理。
      • FastAPI不允许下面代码这种两种情况的返回数据联合混用。如果要支持返回数据联合混用,可以去掉-> Response | dict或使用-> Any ,禁用FastAPI对返回类型的校验。
  1. response_model_exclude_unset=True 设置响应的字段中,只保留实际赋值过的字段,不包含只有默认值的字段。
      • 如果有默认值的字段有过赋值动作,则为实际赋值过的字段,字段将被返回。
      • 如果有默认值的字段赋值与默认值相同,则为实际赋值过的字段,字段将被返回。
  1. response_model_include 接收一个str组成的set,用于指定需要包含的字段,如果用的是listtuple接收值,FastAPI会自动转换为set
  1. response_model_exclude 用于指定需要排除的字段,用法同response_model_include
  1. response_model_exclude_unset=True 优先级高于response_model_includeresponse_model_exclude 优先级高于response_model_include 。下面请求,/itemTest/ 返回{"name": "测试商品"}/itemTest2/ 返回{}
 

17.异常处理

FastAPI中可以自定义异常,也可以覆盖自带的异常处理。
  • RequestValidationError 包含其接收到的带有无效数据的请求体 body
  • FastAPI 的 HTTPException 错误类继承自 Starlette 的 HTTPException 错误类。FastAPI 的 HTTPException 在 detail 字段中接受任意可转换为 JSON 的数据,而 Starlette 的 HTTPException 只接受字符串。
 

18.更新部分字段

更新接口一般用put请求,FastAPI文档推荐更新部分字段用 patch 请求。
上面代码传参{"name": "Barz","price": 3} ,无论是改成 put 还是 post 请求,都能实现只更新传入的字段,只是推荐更新部分字段时,使用 patch 进行请求。请求返回如下:
 

19.依赖项的执行顺序

  • 装饰器依赖项(dependencies参数中的)先执行,参数依赖项后执行。
  • 装饰器依赖项之间,按声明顺序执行,参数依赖项之间同理。
  • 装饰器依赖项的返回值不会传递给路径操作函数,参数依赖项的返回值传递给路径操作函数。
上面测试代码实际执行顺序:['装饰器依赖1', '装饰器依赖2', '参数依赖1', '参数依赖2', '路由函数']。
 

20.带有yield的依赖项

  • 带有 yield 的依赖的退出代码会在响应发送给客户端之后执行。
  • Depends() 接收一个 scope 参数:
    • "function":在处理请求的 路径操作函数 之前启动依赖,在 路径操作函数 结束后结束依赖,但在响应发送给客户端之前。因此,依赖函数将围绕这个路径操作函数执行。
    • "request":在处理请求的 路径操作函数 之前启动依赖(与使用 "function" 时类似),但在响应发送给客户端之后结束。因此,依赖函数将围绕这个请求与响应周期执行。
    • 未指定且依赖包含 yield,则默认 scope 为 "request"
    • 声明一个 scope="request"(默认)的依赖时,任何子依赖也需要有 "request" 的 scope
    • scope 为 "function" 的依赖可以有 scope 为 "function" 和 "request" 的子依赖。
    • 任何依赖都需要能够在子依赖之前运行其退出代码,因为它的退出代码中可能还需要使用这些子依赖。
  • 在带有 yield 的依赖中捕获到了一个异常,除非你抛出另一个 HTTPException 或类似异常,否则你应该重新抛出原始异常
示例代码中异常传播机制:
 

21.中间件

FastAPI创建http请求的中间件:在函数的顶部使用装饰器 @app.middleware("http") 。参数固定位 http
  • 存在多个中间件时:在await call_next(request)之前的代码按中间件添加顺序最外层的先执行,之后的代码按最外层的最后执行。
  • 如果你有使用 yield 的依赖,依赖中的退出代码会在中间件之后运行。
 
【mysql优化001】insert on duplicate语句高并发时造成死锁【python001】develop tips
Loading...