Skip to content
🗂️ 文章分类: Python  
🏷️ 文章标签: FastAPI  
📝 文章创建时间: 2024-04-27
🔥 文章最后更新时间:2025-12-06

[toc]

FastAPI笔记1

python_2025-11-26_102143_921.png

FastAPI 介绍

FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,专为在 Python 中构建 RESTful API 而设计。

FastAPI 特点

  • 高性能: FastAPI是建立在Starlette和Pydantic之上的,利用异步(asynchronous)编程,提供出色的性能。
  • 自动文档生成: 自动生成交互式API文档,支持Swagger UI和ReDoc,让API的理解和测试更加直观。
  • 类型注解支持: 利用Python的类型提示,提供更严格的输入验证和更好的代码提示。
  • 异步支持: 支持异步请求处理,使得处理IO密集型任务更加高效。

FastAPI 使用场景

  • 构建API后端: 用于构建RESTful API,支持前后端分离的Web应用。
  • 微服务架构: 可以作为微服务的后端框架,支持快速开发和部署。
  • 数据处理API: 适用于处理数据,接收和返回JSON数据。
  • 实时通信: 支持WebSocket,适用于实时通信场景。

什么是 Starlette 和 Pydantic ?

FastAPI是建立在Starlette和Pydantic之上的。那么Starlette和Pydantic是什么?

Starlette 是一个轻量级的异步Web库,用于构建高性能、可扩展和易维护的Web应用程序。

  • Starlette是基于Python的asyncio库构建的,因此完全支持异步编程。
  • Starlette的设计目标之一是保持简单和轻量级。
  • Starlette提供了灵活的路由系统,可以根据URL模式将请求路由到相应的处理函数。
  • Starlette内置了异常处理机制,可以捕获和处理应用程序中的异常。 ....

Pydantic 是一个用于数据验证和解析的库。它的主要目标是使数据验证变得简单且易于维护。

  • Pydantic 使用Python的类型注解来定义数据模型,使得代码更具可读性和可维护性。
  • Pydantic 自动验证输入数据是否符合定义的模型规范。
  • Pydantic 能够将原始数据解析成Python对象。
  • Pydantic 可以根据数据模型自动生成文档,包括字段名称、数据类型、默认值等信息。

Starlette和Pydantic和FastAPI的关联。

python_20240427185758.png

  • Starlette是负责FastAPI中的web部分(异步web服务等)。
  • Pydantic是负责FastAPI中的数据模型部分(类型提示等)。

FastAPI 安装

FastAPI 依赖 Python 3.8 及更高版本。

py
# 使用 pip 命令来安装FastAPI
pip install fastapi

另外通过FastAPI构建的web应用程序需要运行在web服务器上。因此我们还需要安装一个 ASGI 服务器,在生产环境上可以使用 Uvicorn。

Uvicorn是一个基于ASGI的高性能Web服务器,专门用于运行ASGI应用程序,即FastAPI构造的应用程序。

py
# 使用 pip 命令来安装uvicorn
pip install "uvicorn[standard]"

第一个 FastAPI 应用

① 创建一个 main.py 的文件,并进行编辑。

py
# 导入fastapi包的FastAPI类
from fastapi import FastAPI

# 创建 FastAPI 应用实例 app。用于定义和管理应用的各个组件
app = FastAPI()

# 在app应用实例上添加一个根路径 / 的路由。get请求的
@app.get("/")
def read_root():
    return {"Hello": "World"}

# 在app应用实例上添加一个路径为 /test 的异步路由。post请求的
@app.post("/test")
async def test():
    return {"test": "test"}

代码解析:

  1. @app.get("xxx")这是一个装饰器。表示当请求路径匹配上路由的路径时,将执行这个装饰器装饰的函数。函数返回值就是请求的响应数据。
  2. async def test(): 意思是该函数是一个异步函数。没有表示是一个同步函数。

② 在main.py文件的当前目录下,命令行中运行以下命令以启动FastAPI应用程序。

sh
# uvicorn 启动命令语法
uvicorn 文件名称:app --reload

# --reload 当文件修改后,会自动重写加载。
# main是要执行的py文件的名称
uvicorn main:app --reload

python_20240427194303.png

默认情况下uvicorn服务器的启动端口为8000

③ 浏览器访问各个请求路径。

python_20240427194510.png

API文档

FastAPI 提供了内置的交互式 API 文档,使开发者能够轻松了解和测试 API 的各个接口。

这个文档是自动生成的,基于 OpenAPI 规范,支持 Swagger UI 和 ReDoc 两种交互式界面。

  • 你可以通过访问 http://127.0.0.1:8000/docs 来打开 Swagger UI 风格的文档。
  • 你也可以通过访问 http://127.0.0.1:8000/redoc 来打开 ReDoc 风格的文档。

Swagger UI 风格的文档 python_20240427212313.png

ReDoc 风格的文档 python_20240427212239.png

FastAPI应用的启动端口和启动IP

默认情况下uvicorn服务器的启动端口为8000,启动IP为127.0.0.1

下面代码可以进行更改

sh
# --reload 当文件修改后,会自动重写加载。
# main是要执行的py文件的名称
# --host 启动ip
# --port 启动端口
uvicorn main:app --host 0.0.0.0 --port 38080 --reload

路径操作装饰器

@something语法在 Python 中被称为「装饰器」。

而在 FastAPI 中,每个路由都通过路径操作装饰器来映射到程序中的一个函数中。

简而言之,路径操作装饰器用来绑定路由和函数。

常见的路径操作装饰器如下

py
from fastapi import FastAPI
app = FastAPI()

## 四种HTTP请求方式的装饰器
@app.get()
@app.post()
@app.put()
@app.delete()
## 请求参数相关的装饰器
@app.options()
@app.head()
@app.patch()
@app.trace()

路径操作装饰器的参数

我们可以给路径操作装饰器中传入许多参数。从而实现不同的效果。

常用的装饰器参数如下。更多的参数请查询文档。

py
from fastapi import FastAPI
app = FastAPI()
# 此处以post请求的路径操作装饰器举例
@app.post(
    path,
    response_model,
    status_code,
    tags,
    summary="xxx",
    description="xxx",
    response_description="xxx",
    responses,
    deprecated=False
    ....
)

tags,summary,description等参数

tags,summary,description等参数主要用于给文档中的接口添加详细信息。

例子

py
@app.post("/test",tags=["这是test接口的tag"],
          summary="这是test接口的summary",
          description="这是test接口的description",
          response_description="这是test接口的response_description")
def test():
    return {"Hello": "World"}

文档中的效果如下图所示。 python_20240430232411.png

FastAPI 路由

APIRouter函数

在上面的第一个 FastAPI 应用的代码中。我们是先通过FastAPI()函数创建出FastAPI应用实例,然后直接在FastAPI应用实例上添加路由。如下面代码所示。

py
# 导入fastapi包的FastAPI类
from fastapi import FastAPI
# 创建 FastAPI 应用实例 app。用于定义和管理应用的各个组件
app = FastAPI()

# 在app应用实例上添加一个根路径 / 的路由
@app.get("/")
def read_root():
    return {"Hello": "World"}

但是这种方式不规范,这种方式是直接把FastAPI应用实例和路由进行绑定,从而导致无法区分不同种类路由。因此FastAPI提供了一个APIRouter函数,专门用于路由管理。

如下面代码所示

py
# 导入fastapi包的FastAPI类
from fastapi import FastAPI,APIRouter

# 创建 FastAPI 应用实例。用于定义和管理应用的各个组件
app = FastAPI()

# 创建路由实例
router = APIRouter()

# 创建根路由
@router.get("/")
def test():
    return {"Hello": "World"}
# 创建/test路由
@router.get("/test")
def test():
    return {"test": "test"}


# 通过include_router函数,把应用实例app与路由实例router绑定
app.include_router(router)
  1. router = APIRouter()APIRouter函数用来创建路由实例。之后再路由实例上添加具体的路由。
  2. app.include_router(router) 通过include_router函数,把应用实例app与路由实例router绑定在一起。

通过这种方式,我们可以把FastAPI的应用实例和路由实例分离开来。从而降低代码之间的耦合度。

include_router函数

如上面代码所示,include_router函数可以将应用实例与路由实例绑定。除此之外,include_router函数还可以给不同分类的路由添加前缀,标签,注释等信息。

py
from fastapi import FastAPI,APIRouter
app = FastAPI()

router = APIRouter()
@router.get("/test")
def test():
    return {"Hello": "World"}

app.include_router(router,prefix="/module01",tags=["这是模块01的接口"])

python_20240502000141.png

多个模块的路由管理

在正式项目中,一个FastAPI应用通常由多个模块(多个包)组件的。每个模块都拥有自己的路由。

假设一个FastAPI应用的工程目录结构如下所示。

├── fastapi-app
│   ├── __init__.py
│   ├── main.py
│   └── module01
│   │   ├── __init__.py
│   │   └── module01Routers.py
│   └── module02
│   │   ├── __init__.py
│   │   └── module02Routers.py

模块01的module01Routers.py文件代码如下

py
from fastapi import APIRouter
router = APIRouter()

# 用户模块的测试接口
@router.get("/user/test")
async def usertest():
    return {"test": "test"}

模块02的module02Routers.py文件代码如下

py
from fastapi import APIRouter
router = APIRouter()

# 订单模块的测试接口
@router.get("/order/test")
async def ordertest():
    return {"test": "test"}

main.py文件代码如下

py
# 导入FastAPI
from fastapi import FastAPI
# 创建FastAPI应用实例
app = FastAPI()

# 导入各个模块
from module01 import module01Routers
from module02 import module02Routers

# 通过include_router函数,把各个模块的路由实例加入到FastAPI应用实例中
app.include_router(module01Routers.router,prefix="/module01",tags=["这是模块01的接口"])
app.include_router(module02Routers.router,prefix="/module02",tags=["这是模块02的接口"])

通过上面的方式,各个模块的路由分开编写,并且通过include_router函数整合到FastAPI应用实例中。

python_20240502000027.png

FastAPI 请求

FastAPI 提供了多种方式来获取不同类型的请求参数,并根据需要生成规范的响应。

查询参数

通常情况下查询参数是指请求路径URL中?后面拼接的键值对字符串。格式为key=value&key2=value2

假设请求路径URL如:http://127.0.0.1:8000/test?a=1&b=2。那么请求中?后面的a=1&b=2键值对就是查询参数a和b。

默认情况下路由函数形参需要与查询参数同名。这样形参才能接收到查询参数的数据。

py
from fastapi import APIRouter
router = APIRouter()

@router.get("/test")
def test(a,b):
    return {"a": a, "b": b}

# 给函数参数设置默认值
@router.get("/test2")
# a: int = 0 把参数a的类型设置为整数类型。默认值为 0。
def test2(a: int = 0, b: int = 10):
    return {"a": a, "b": b}

# 将函数参数设置为可选参数
@router.get("/test3")
def test3(a: int = 0, b: int = None):
    return {"a": a, "b": b}

python_20240502184110.png

路径参数

路径参数是直接包含在请求 URL 路径中的参数。路径参数是以占位符的形式出现的,格式为 http://127.0.0.1:8000/items/{item_id}

当函数中的形参与路径参数同名的时候,函数形参才能接受到路径参数的数据。

py
from fastapi import APIRouter
router = APIRouter()

# /items/{item_id}  定义了一个请求路径,其中{item_id} 是路径参数,对应于函数参数 item_id。
@router.get("/items/{item_id}")
# 函数参数item_id,数据类型为int
def read_item(item_id: int):
    return {"item_id": item_id}

python_20240502180157.png

请求头参数

有些情况下,我们想要可以获取请求头中的各个数据。例如请求头 header,请求url,cookis等。

以下有两种方式可以获取请求头参数

方式1:使用 Header 类

py
from fastapi import APIRouter
from fastapi import Header
router = APIRouter()

@router.get("/get_header")
# 直接在函数中定义参数。参数名需要与请求头中的键名相同。
def get_header(user_agent: str = Header(None), x_custom_header: str = Header(None)):
    return {"User-Agent": user_agent, "X-Custom-Header": x_custom_header}

方式2:使用Request对象来获取请求头参数

在FastAPI中我们可以使用 Request 类来接收请求对象。

py
from fastapi import APIRouter,Request
router = APIRouter()

@router.post("/getRequest")
# req参数类型为Request类型。用来接收请求信息。
def getRequest(req: Request):
    return {
        "RequestURL":req.url,          # 请求URL
        "RequestIP":req.client.host,    # 请求IP
        "RequestHeader":req.headers,    # 请求头
        "RequestCookie":req.cookies,    # 请求cookie
        "Authorization":req.headers.get("Authorization"),  # Authorization请求头
        "user-agent":req.headers.get("user-agent")  # user-agent请求头
    }

python_20240502231623.png

Cookie参数

还可以通过Cookie类来获取请求头中的cookie参数。

代码示例

python
from fastapi import Cookie

@router.get("/get_cookie")
def get_cookie(cookie: str = Cookie(None)):
    return {"cookie": cookie}

请求体参数-form表单类型数据

通常情况下,GET请求是没有请求体的。因此GET请求只能携带查询参数。而POST请求等是有请求体的。

FastAPI如何接受POST请求中请求体的form表单类型数据?

需要先在FastAPI中可以使用 python-multipart 包来处理表单数据。

用法

步骤① 先安装 python-multipart 包

pip install python-multipart

步骤② 使用 python-multipart 包的Form()函数获取form表单数据

py
# 引入Form函数
from fastapi import APIRouter,Form
router = APIRouter()

# post请求
@router.post("/login")
def login(username: str = Form(),password: str = Form()):
    return {
        "username":username,
        "password":password
    }
  • Form()函数会拿到请求体中的表单数据。
  • username: str = Form() 表示参数username,类型为字符串。参数数据通过Form()函数来获取。

可以看到图中的Content-Type是application/x-www-form-urlencoded。这表示数据是Form表单数据。

python_20240502194448.png

请求体参数-json类型数据

通常情况下,GET请求是没有请求体的。因此GET请求只能携带查询参数。而POST请求等是有请求体的。

FastAPI如何接受POST请求中请求体的json数据?

有两种方式可以获取到请求体中的json数据。

方式1 使用 Pydantic 模型类

在FastAPI中我们可以使用pydantic包来创建数据模型类。通过数据模型类来接收请求体中的json数据。

注意:只有自定义的数据模型类继承pydantic包中的BaseModel类。才能将请求中体的json数据进行校验并传递给模型类对象。

py
from pydantic import BaseModel
from fastapi import APIRouter
router = APIRouter()

# 定义Addr模型类型。该类需要继承pydantic包中的BaseModel类
class Addr(BaseModel):
    prpvince:str
    city:str

# 定义数据模型Item类及其属性。该类需要继承pydantic包中的BaseModel类
class Item(BaseModel):
    name: str
    age: int = 10               # age属性设置默认值10
    description: str = None     # 设置为可选属性
    price: float
    tax: float = None           # 设置为可选属性
    address: Addr               # 地址属性类型为Addr

# post请求
@router.post("/create_item")
# 函数参数item,数据类型为Item类。
# 该参数可以接收一个请求体数据,数据格式需要与Item类相同。
def create_item(item: Item):
    return item

python_20240502191747.png

方式2 使用 Body 方法

在FastAPI中我们可以使用Body方法来接收请求体中的json数据。

py
from fastapi import APIRouter,Body
router = APIRouter()

# post请求
@router.post("/create_item")
# 函数参数name,数据类型为字符串。参数数据通过Body()函数来获取。
# Body(...) 表示参数必填,请求体中必须包含该字段。否则返回422错误。
# Body() 默认等价与 Body(...) 表示参数必填。
# Body(None) 表示参数可选,请求体参数可以不包含该字段。
def create_item(name: str = Body(...),password: str = Body(None),age: int = Body()):
    return {"name":name,"password":password,"age":age}

Body()方法的参数个数

当使用Body()方法接收参数时,FastAPI的行为会根据参数数量而变化。

  • 当路由函数只有一个Body()参数时,FastAPI会尝试将整个请求体数据直接解析为该参数的类型。
  • 当路由函数有多个Body()参数时, FastAPI会将整个请求体解析为一个JSON对象,然后从中提取对应的字段值分配给各个参数。

单个Body()参数时

问题代码: 当请求体参数为{"name":"xiaoming"}时。那么该函数会报422错误。因为name参数无法接收整个json数据

python
def test(name: str = Body(None)):
    pass

此处函数test只有一个Body()参数name。请求体数据为json格式。而name参数为字符串。FastAPI无法将整个json数据直接解析为字符串。因此会报422错误。

解决方法①:可以将参数类型设置为字典类型。从而让name参数接收整个json数据。

python
def test(name: dict = Body(None)):
    print(name) # 此处会打印 {"name":"xiaoming"}
    pass

解决方法②:给Body()方法添加embed=True参数

python
def test(name: str = Body(None,embed=True)):
    print(name) # 此处会打印 {"name":"xiaoming"}
    pass

embed=True参数告诉FastAPI将单个参数嵌入到请求体中,而不是尝试将整个请求体解析为该参数的类型。

多个Body()参数时

python
# 当请求体参数为{"name":"xiaoming","age":18}时。那么该函数会正常运行。
def test(name: str = Body(None),age: int = Body(None)):
    print(name) # 此处会打印 xiaoming
    print(age)  # 此处会打印 18
    pass

Body()方法的几种使用方式

Body()方法以下几种使用方式。

① Body(...) 必填参数(最严格)

使用省略号...表示参数必填,请求体中必须包含该字段。

  • 如果请求参数缺少该字段。会返回 422 错误。
  • 如果字段值为 null(JSON 中的 null)。也会返回 422 错误(因为类型校验失败)。

示例

python
# phone 和 password 必须在请求体中提供
def phoneLogin(phone: str = Body(...), password: str = Body(...)):
    pass

② Body() 必填参数(等价于 Body(...))

如果没有提供参数,则会返回422错误。

python
# phone 和 password 字段必须在请求体参数中提供(与 Body(...) 效果相同)
def phoneLogin(phone: str = Body(), password: str = Body()):
    pass

③ Body(None) 可选参数

显式设置默认值为 None,表示参数可选,请求体参数可以不包含该字段。

  • 如果请求体缺少该字段。使用默认值 None,不会报错。
  • 如果请求体包含该字段但值为 null,也会接收为 None。
python
# address 可选,默认值为 None
def login(address: str = Body(None)):
    pass

④ Body("") 可选参数

显式设置默认值为空字符串,表示参数可选,请求体参数可以不包含该字段。

文件上传参数

py
from fastapi import File, UploadFile

@router.post("/upload_file")
def upload_file(file: UploadFile = File(...)):
    return {
        "filename": file.filename,
        "content_type": file.content_type
    }
  • file: UploadFile = File(...) 表示参数file,类型为UploadFile类。参数数据通过File()函数来获取。
  • UploadFile类会自动解析请求体中的文件数据。

请求参数验证和默认值

FastAPI 支持为请求参数添加验证规则和默认值。

python
from fastapi import Query, Path

@router.get("/items/{item_id}")
# 函数参数item_id,数据类型为整数。参数数据通过路径来获取。ge=1, le=1000 表示参数值必须在1到1000之间。description="项目ID" 表示参数的描述信息。
def read_item(
    item_id: int = Path(..., ge=1, le=1000, description="项目ID"),
    q: str = Query(None, min_length=3, max_length=50, description="查询字符串")
):
    return {"item_id": item_id, "q": q}
  • Path()函数表示路径参数。ge 表示参数最小值,le表示参数最大值。descriptio 表示参数的描述信息
  • Query()函数表示查询参数。min_length表示参数最小长度,max_length表示参数最大长度。

多种请求参数混合 ★★★

各种类型参数组合

FastAPI 支持在同一个函数中混合使用多种参数类型,包括查询参数、路径参数、请求体参数、Cookie参数、Header参数、文件上传参数等。

代码示例如下

python
@router.post("/items/{item_id}")
def create_item(
    item_id: int,
    query_param: str = "default",
    request_data: Item = Body(...),
    user_agent: str = Header(None)
):
    return {
        "item_id": item_id,
        "query_param": query_param,
        "item_name": request_data.name,
        "user_agent": user_agent
    }
  • item_id: int 表示路径参数item_id,类型为整数。参数数据通过路径来获取。
  • query_param: str = "default" 表示查询参数query_param,类型为字符串。参数数据通过查询参数来获取。默认值为"default"。
  • request_data: Item = Body(...) 表示请求体参数request_data,类型为Item类。参数数据通过Body()函数来获取。
  • user_agent: str = Header(None) 表示Header参数user_agent,类型为字符串。参数数据通过Header()函数来获取。默认值为None。

请求Json数据的多字段混合

在FastAPI中我们可以使用Pydantic模型类来接收请求体json数据。

但是有时候请求体json数据,不仅仅包含了Pydantic模型类中定义的属性,还包含了其他额外的字段。此时如果我们直接将请求体json数据赋值给模型类对象,会产生422 Unprocessable Entity错误。

代码示例如下

python
# 请求体json数据如下
{
    "aaa":"123",
    "bbb":"shuyx",
    "ccc":"123456"
}
# dto类型如下
class Item(BaseModel):
    aaa: str
    aaa: str

# 路由函数如下
@router.post("/test")
def test(item: Item):
    return item

当发送请求体json数据给路由函数test时,会产生422 Unprocessable Entity错误。是因为请求数据中包含了Pydantic模型类中没有定义的属性ccc,从而导致函数的参数item无法解析请求体json数据。

目前有两种解决方式。

方式1

不使用 Pydantic模型类 来接受请求体json数据,直接使用字典来接受。

更改示例如下

py
# 请求体json数据如下
{
    "aaa":"123",
    "bbb":"shuyx",
    "ccc":"123456"
}
# post请求
@router.post("/test")
# 函数参数aaa,数据类型为字符串。参数数据通过Body()函数来获取。
# 其他参数同理
def test(aaa: str = Body(...),bbb: str = Body(...),ccc: str = Body(...)):
    return item

方式2

对于这种一个请求体json数据中包含了Pydantic模型类中没有定义的属性。我们需要同时更改请求json数据和路由函数的参数。

更改示例如下

python
# 更改后的请求体json数据如下
{
    "item":{
        "aaa":"123",
        "bbb":"shuyx",
    },
    "ccc":"123456"
}
# dto类型如下
class Item(BaseModel):
    aaa: str
    bbb: str

# 更改后的路由函数如下
@router.post("/test")
# ccc: str = Body(...) 表示请求体参数ccc,类型为字符串。参数数据通过Body()函数来获取。
# item: Item = Body(...) 表示请求体参数item,类型为Item类。参数数据通过Body()函数来获取。
def test(ccc: str = Body(...), item: Item = Body(...)):
    return item

通过将json数据分为两大部分,分别是itemcccitem部分包含了Pydantic模型类中定义的属性。ccc部分包含了额外的字段ccc

这样路由函数参数就可以同时获取到itemccc两个部分的参数数据。

注意:json数据中的key必须与路由函数参数的名称一致。

422 Unprocessable Entity 错误

422 Unprocessable Entity 是一个 HTTP 状态码,表示服务器理解请求的语法,但无法处理请求的内容(参数验证失败、缺少必填字段等)

在 FastAPI 中,422 错误主要由 Pydantic 模型验证失败或参数验证失败引起。

422 错误的常见场景

  • 函数参数使用 Body(...)、Query(...) 等标记为必填参数,但请求中缺少该参数。
  • 函数参数中使用了单个Body()参数,但是请求体json数据中包含了1个字段。导致FastAPI无法将整个json数据直接解析为该参数类型。
  • 请求中的参数类型与函数定义的类型不匹配。
  • 使用 Pydantic 模型作为函数参数时,请求数据不符合 Pydantic 模型定义(缺少必填字段、字段类型错误等)。
  • 参数值超出了定义的限制范围。
  • 路径参数或查询参数与函数定义不匹配。
  • 文件上传参数验证失败(文件类型、大小等)。

解决方式如下

  • 使用 Pydantic 模型定义请求体和响应体,利用其强大的验证功能。
  • 直接使用字典来接受请求体json数据。
  • 对于这种一个请求体json数据中包含了Pydantic模型类中没有定义的属性。我们需要同时更改请求json数据和路由函数的参数。

FastAPI 响应

FastAPI 提供了多种响应方式,可以根据不同的场景选择合适的响应类型。

直接响应Python 对象(如字典、列表等)- 转换为Json数据

路由处理函数直接返回 Python 对象(字典、列表等),FastAPI 会自动将其转换为 JSON 数据并响应给客户端。

py
from fastapi import APIRouter
router = APIRouter()

@router.get("/test1")
def test1(a, b):
    return {"a": a, "b": b}  # 返回字典数据

返回的数据格式如下

json
{
  "a": "xxx",
  "b": "xxx",
}

Pydantic数据模型 - 转换为Json数据

如果路由处理函数返回一个 Pydantic 数据模型实例对象,FastAPI 会自动将其转换为 JSON 格式数据,并响应给客户端。

py
from fastapi import APIRouter
from pydantic import BaseModel
router = APIRouter()

# 定义 Pydantic 数据模型类。该类需要继承pydantic包中的BaseModel类。
# 并且类中的属性需要符合Pydantic数据模型的要求。
class Item(BaseModel):
    name: str
    description: str = None

@router.post("/items/")
def get_item(item: Item):
    return item  # 返回数据模型类数据

返回的数据格式如下

json
{
  "name": "xxxx",
  "description": "xxxxxxx",
}

Response 类 - 基础响应类

Response类是FastAPI提供的最基础的响应类,可以完全自定义响应内容、状态码和头信息。

py
from fastapi.responses import Response

@app.get("/raw")
async def raw_response():
    return Response(
        content="自定义原始内容",
        media_type="text/plain",
        status_code=200,
        headers={"X-Custom-Header": "custom-value"}
    )

HTMLResponse 类 - 响应HTML内容

HTMLResponse 类用于响应HTML内容。它可以将HTML字符串作为响应体返回给客户端。

py
from fastapi.responses import HTMLResponse

@app.get("/html")
async def html_response():
    html_content = """
    <html>
        <head>
            <title>HTML响应示例</title>
        </head>
        <body>
            <h1>Hello, FastAPI!</h1>
        </body>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)

JSONResponse 类 - 响应JSON内容

JSONResponse 类用于响应JSON内容。它可以将Python对象(如字典、列表等)转换为JSON格式数据,并响应给客户端。

py
from fastapi.responses import JSONResponse

@app.get("/custom-json")
async def custom_json():
    return JSONResponse(
        status_code=201,
        content={"message": "资源创建成功"},
        headers={"X-Custom-Header": "value"}
    )

RedirectResponse类 - 重定向响应

使用 RedirectResponse类可以实现重定向响应,将客户端重定向到其他路由上。

py
from fastapi.responses import RedirectResponse
from pydantic import BaseModel
from fastapi import APIRouter
router = APIRouter()

@router.get("/a")
def a():
    return {"a": "a"}

@router.get("/redirect")
def redirect():
    return RedirectResponse(url="/a")  # 重定向到/a路由上

在浏览器访问/redirect路由的时候会自动跳转到 /a路由。

FileResponse 类 - 响应文件内容

FileResponse 类用于响应文件内容。它可以将文件内容作为响应体返回给客户端。

py
from fastapi.responses import FileResponse
import os

@app.get("/download/{filename}")
async def download_file(filename: str):
    file_path = f"/path/to/files/{filename}"
    if os.path.exists(file_path):
        # 使用FileResponse类返回文件内容
        return FileResponse(
            path=file_path,
            filename=filename,  # 下载时的文件名
            media_type="application/octet-stream"
        )
    return {"error": "文件不存在"}

StreamingResponse 类 - 响应流式数据

StreamingResponse 类用于响应流式数据。它可以将流式数据(如生成器函数、异步迭代器等)作为响应体返回给客户端。

py
from fastapi.responses import StreamingResponse
import io
import time

# 定义一个生成器函数,用于模拟流式数据的生成
def generate_data():
    for i in range(10):
        yield f"数据块 {i}\n"
        time.sleep(0.5)  # 模拟处理延迟


@app.get("/stream")
async def stream_data():
    # StreamingResponse类,将生成的数据作为流式响应返回
    return StreamingResponse(generate_data(), media_type="text/plain")

在浏览器访问/stream路由的时候,会看到类似如下的响应内容:

数据块 0
数据块 1
数据块 2
数据块 3
数据块 4

响应参数

可以在路径操作装饰器中添加各种响应参数来实现各种功能。

response_class 参数 - 设置响应类

python
# 设置全局的默认响应类
app = FastAPI(default_response_class=JSONResponse)

# 设置该路由函数的响应类
@router.get("/html", response_class=HTMLResponse)
async def get_html():
    return "<h1>HTML内容</h1>"

当设置了响应类后,FastAPI会根据响应类的定义来自动处理响应数据。

response_model 参数 - 指定响应数据格式

FastAPI提供了response_model 参数。可以在路径操作装饰器中使用response_model参数来定义响应模型数据的格式。

py
from fastapi import FastAPI
app = FastAPI()

from pydantic import BaseModel
from fastapi import APIRouter
router = APIRouter()

# 定义类ItemA
class ItemA(BaseModel):
    username: str
    password: str
    age: int

# 定义类ItemB
class ItemB(BaseModel):
    username: str
    age: int

@router.post("/getItem",response_model=ItemB)
def getItem(item: ItemA):
    return item

# app应用实例中添加路由实例
app.include_router(router)
  • item: ItemA 函数形参接收的是ItemA类对象。
  • response_model=ItemB 通过给路径操作装饰器添加response_model参数。来定义响应数据格式为ItemB类。

运行结果如图所示 python_20240503003311.png

在上面的代码中,定义了两个模型类ItemA和ItemB。路由处理函数getItem()接收并返回的都是ItemA类对象。但是最终的响应结果数据确是ItemB类对象。

简而言之,若路径操作装饰器中添加了response_model参数。那么FastAPI会把函数的返回值数据转换为response_model参数声明的数据类型。

response_model_exclude_unset 参数 - 去除未被赋值的响应字段

response_model_exclude_unset 参数可以去除响应模型中未被赋值的字段。即响应模型中没有被赋值的字段,不会添加到响应数据中。

py
@router.post("/getItem",response_model=ItemB,response_model_exclude_unset=True)
def getItem(item: ItemA):
    return item

函数返回的是ItemA类型的数据。但是当ItemA类型返回数据中没有被赋值的字段,不会添加到响应数据中。

response_model_include 参数 - 精细控制包含响应字段

response_model_include参数可以让你更精细地控制响应数据中包含哪些字段。

py
@router.post("/getItem",response_model=ItemB,response_model_include={"username", "age"})
def getItem(item: ItemA):
    return item

函数返回的是ItemA类型(有三个属性字段)的数据。但是response_model_include参数可以控制响应数据中只包含两个字段(username,age)的数据。

response_model_exclude 参数 - 精细控制不包含响应字段

response_model_include参数可以让你更精细地控制响应数据中不包含哪些字段。

py
@router.post("/getItem",response_model=ItemB,response_model_exclude={"password"})
def getItem(item: ItemA):
    return item

函数返回的是ItemA类型(有三个属性字段)的数据。但是response_model_include参数可以控制响应数据中排除password字段数据。

HTTPException 类 - 响应异常信息

使用 HTTPException 可以抛出异常,返回自定义的状态码和详细信息。

py
from fastapi import HTTPException
from fastapi import APIRouter
router = APIRouter()

@router.get("/items")
def read_item():
    # 抛出HTTPException异常,参数为异常信息
    raise HTTPException(status_code=404, detail="Item not found")

自定义响应数据

使用 JSONResponse类 可以自定义响应数据。

py
from fastapi.responses import JSONResponse
from fastapi import APIRouter
router = APIRouter()

@router.get("/items/{item_id}")
def read_item(item_id: int):
    content = {"item_id": item_id}
    headers = {"X-Custom-Header": "custom-header-value"}
    return JSONResponse(content=content, headers=headers)

文件上传

当我们需要进行文件上传的时候,需要把请求的content-type设置为multipart/form-data格式。这个格式是表单文件上传格式。

下面是文件上传的两种方式。

小文件上传

使用fastapi包中的File函数来获取文件对象

py
from fastapi import APIRouter,File
router = APIRouter()

# 单个文件上传请求 (适合小文件上传)
@router.post("/uploadFile")
def uploadFile(file: bytes = File()):
    return {
        "fileLength":len(file)      # 文件字节大小
    }

# 多个文件上传请求  (适合小文件上传)
@router.post("/uploadFiles")
def uploadFiles(files: list[bytes] = File()):
    return {
        "fileCount":len(files)      # 文件个数
    }
  • File函数用来获取上传文件的数据。
  • file: bytes = File() 设置一个file参数,类型为字节数据类型。数据从File函数获取。

注意:这种方式是一次性读取文件数据内存中,因此这种方式只适合小文件上传。对于大文件上传需要用其他方法。

大文件上传 (推荐)

使用fastapi包中的UploadFile函数来获取文件对象。

py
from fastapi import APIRouter,UploadFile
router = APIRouter()

# 单个文件上传请求 
@router.post("/uploadFile")
def uploadFile(file: UploadFile):
    return {
        "fileSize":file.size,         # 文件大小
        "fileName":file.filename        # 文件名称
    }

# 多个文件上传请求
@router.post("/uploadFiles")
def uploadFiles(files: list[UploadFile]):
    # 打印文件其他信息
    for i in files:
        print("fileName",i.filename)
        print("fileSize",i.size)
    return {
        "fileCount":len(files)      # 文件个数
    }
  • file: UploadFile 设置一个file参数,是UploadFile类型。

通过UploadFile函数,我们还可以获取文件名称和大小等其他信息。

python_20240502222728.png

FastAPI 访问静态文件

我们可以在FastAPI应用中开放一个静态文件目录。通过这个目录来专门访问FastAPI应用中的静态文件。

  1. main.py文件编写如下代码
py
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
app = FastAPI()

# FastAPI应用实例对象 挂载静态目录
app.mount("/statics",StaticFiles(directory="statics"))
  • 先导入fastapi.staticfiles包中的StaticFiles类。
  • StaticFiles(directory="statics") 设置statics目录为静态文件目录。之后挂载到FastAPI应用实例对象app上。
  • 注意:静态文件目录和代码文件之间的相对路径。
  1. 然后创建statics目录。其中创建一个txt文件
  2. 浏览器访问静态文件。

python_20240502235451.png

Released under the MIT License.