
目录一、前言二、FastAPI 介绍2.1 FastAPI 是什么2.2 FastAPI 特点2.3 FastAPI 适用场景2.4 FastAPI 技术栈2.5 为什么选择 FastAPI三、FastAPI 安装与基本使用3.1 前置准备3.1.1 版本检查3.1.2 创建虚拟环境3.1.3 安装 FastAPI3.1.4 安装 Uvicorn3.2 FastAPI使用3.2.1 第一个 FastAPI 程序3.2.2 启动服务3.2.3 接口效果验证3.2.4 接口文档3.2.5 使用FastAPI CLI 启动服务3.2.6 使用uvicorn 启动四、FastAPI 接口请求参数使用4.1 接口参数4.1.1 接口参数说明4.1.2 接口参数分类4.2 路径参数4.2.1 路径参数类型4.2.2 路径顺序4.2.3 预设值的路径参数Enum4.2.4 包含路径的路径参数4.3 查询参数4.3.1 基本用法4.3.2 必选查询参数4.3.3 参数混合使用4.4 查询参数校验4.4.1 参数基本校验4.4.2 同时添加更多参数校验4.4.3 带默认值的校验4.4.4 必填参数4.5 路径参数值校验4.5.1 Path导入4.5.2 Path 校验参数说明4.5.3 数值组合校验4.5.4 路径参数与查询参数混合使用4.5.5 小结4.6 请求体参数4.6.1 什么是请求体参数4.6.2 请求体与查询参数的区别4.6.3 使用 Pydantic 模型声明请求体4.6.4 请求体参数-类型注解Field4.6.5 请求体 路径参数 查询参数混合4.6.6 小结五、写在最后一、前言在微服务开发中通常需要服务端提供接口给前端接口将来自各处的数据汇总在一起返回给页面做展示在Python 中有很多种WEB框架可以构建API接口本文以比较热门的FastAPI 为例进行说明。二、FastAPI 介绍2.1 FastAPI 是什么FastAPI 是一个用于构建 API 的现代、快速高性能的 Python Web 框架专为构建 RESTful API 而设计。官网FastAPI - FastAPIFastAPI 使用 Python 3.8 并基于标准的 Python 类型提示使用 Starlette 和 Pydantic 构建能够自动生成 API 文档并进行数据校验。2.2 FastAPI 特点FastAPI 之所以在 Python Web 框架中脱颖而出主要得益于以下特点高性能基于 Starlette 和 Pydantic性能与 NodeJS 和 Go 相当是最快的 Python 框架之一快速开发开发速度提升约 200%-300%标准类型声明即可完成数据校验和文档生成减少错误减少约 40% 的人为错误类型系统自动捕获常见问题自动生成文档自动生成交互式 API 文档Swagger UI 和 ReDoc无需手动维护类型安全基于标准 Python 类型提示编辑器提供全面的自动补全和错误检查异步支持原生支持 async/await可高效处理 IO 密集型任务2.3 FastAPI 适用场景在下面的一些开发场景中可以考虑使用FastAPI构建 API 后端用于构建 RESTful API支持前后端分离的 Web 应用微服务架构轻量高效适合作为微服务后端框架数据处理 API适用于接收和返回 JSON 数据的数据处理服务实时通信支持 WebSocket适用于实时通信场景机器学习服务可将训练好的模型封装为 API方便前端和其他服务调用2.4 FastAPI 技术栈FastAPI 构建在两个核心库之上FastAPI 是 Starlette 的子类因此你可以使用 Starlette 的所有功能同时 FastAPI 完全兼容 Pydantic包括基于 Pydantic 的 ORM如 SQLModel等外部库组件作用说明StarletteWeb 框架层提供路由、中间件、WebSocket 等基础 Web 功能FastAPI 直接继承自 StarlettePydantic数据校验层基于 Python 类型提示进行数据校验、序列化和文档生成UvicornASGI 服务器基于 uvloop 和 httptools 的高性能 ASGI 服务器用于运行 FastAPI 应用2.5 为什么选择 FastAPIFastAPI 作为一款优秀的WEB框架相对其他框架有很多优点下面多维度对比了其他Python框架的特点对比维度FastAPIFlaskDjango性能高异步ASGI中同步WSGI中同步WSGI自动文档内置Swagger UI ReDoc需第三方扩展需第三方扩展类型校验内置Pydantic需手动实现需手动实现异步支持原生支持需扩展3.1 支持学习曲线低低较高适用规模中小型 / 微服务中小型大型 / 全栈三、FastAPI 安装与基本使用3.1 前置准备3.1.1 版本检查FastAPI 依赖 Python 3.8 及更高版本因此首先要检查你本地的Python 版本是否符合要求如果你的 Python 版本低于 3.8请先升级 Python3.1.2 创建虚拟环境推荐在虚拟环境下安装 FastAPI避免与系统中已有的 Python 包产生冲突虚拟环境是 Python 开发最佳实践每个项目可以使用独立的虚拟环境可避免不同项目之间的依赖冲突# 创建虚拟环境 python -m venv venv # 激活虚拟环境macOS/Linux source venv/bin/activate # 激活虚拟环境Windows venv\Scripts\activate3.1.3 安装 FastAPI使用 pip 命令安装 FastAPIpip install fastapi使用上面这条命令只安装 FastAPI 核心包如果你需要一次性安装 FastAPI 及其所有可选依赖可以使用下面这个命令pip install fastapi[all]fastapi[all]会安装以下组件包名用途uvicorn[standard]ASGI 服务器用于运行 FastAPI 应用python-multipart表单数据和文件上传支持jinja2HTML 模板引擎python-jose[cryptography]JWT 令牌支持passlib[bcrypt]密码哈希与加密python-dotenv环境变量管理3.1.4 安装 UvicornFastAPI 是一个 ASGI 框架需要一个 ASGI 服务器来运行最常用的是 Uvicorn[standard]会安装 uvloop高性能事件循环和 httptools高性能 HTTP 解析器显著提升性能ASGIAsynchronous Server Gateway Interface是 Python 异步 Web 服务器与应用程序之间的标准接口是 WSGI 的异步版本。传统框架如 Flask 使用 WSGI同步而 FastAPI 使用 ASGI异步能够更高效地处理并发请求pip install uvicorn[standard]3.2 FastAPI使用使用FastAPI编写接口的完整流程导入FastAPI创建FastAPI实例对象创建路径操作函数定义访问路径运行FastAPI服务fastapi dev xxxx.pyuvicorn xxxx:app--reload在main函数中使用下面这种方式启动uvicorn.run(app, host0.0.0.0, port8000)3.2.1 第一个 FastAPI 程序上面的命令安装完成FastAPI后创建一个main.py文件在文件中添加如下3个接口代码from fastapi import FastAPI # 创建 FastAPI 应用实例 app FastAPI() # 定义根路径的 GET 请求 app.get(/) async def root(): return {message: Hello World} # 定义 /items/{item_id} 路径的 GET 请求 app.get(/items/{item_id}) async def get_item(item_id: int): return {item_id: item_id, name: fItem {item_id}} # 定义 /users/ 路径的 POST 请求 app.post(/users/) async def create_user(name: str, age: int): return {name: name, age: age, message: User created successfully}3.2.2 启动服务通过命令行定位到当前的这个main.py文件然后执行下面的命令把服务运行起来uvicorn main:app --reload看到下面的效果说明启动成功启动参数说明main:appmain 指文件名 main.pyapp 指文件中创建的 FastAPI 实例变量名--reload开发模式代码修改后自动重载服务器仅用于开发环境3.2.3 接口效果验证分别测试一下几个接口1、第一个接口2、第二个接口3、第三个接口3.2.4 接口文档同时服务启动之后FastAPI 自动生成了交互式 API 文档访问交互式文档Swagger UI: High Performance Web Crawler API - Swagger UIReDoc: High Performance Web Crawler API - ReDoc3.2.5 使用FastAPI CLI 启动服务FastAPI 新版本提供了fastapi命令行工具可以更方便地运行应用需要先安装下面的依赖pip install fastapi[standard]安装完成后可以使用下面的命令启动服务# 开发模式自动重载适用于日常开发或者开发环境的服务启动 # 当你修改代码后服务器会自动重启让你能立即看到效果 fastapi dev 或者fastapi dev main.py # 生产模式不能自动重载适用于线上环境服务启动 # 出于性能考虑默认关闭了自动重载功能 fastapi run 或者fastapi run main.py比如我启动开发模式下的服务再次测试一下仍然可以正常访问补充无论是哪个命令FastAPI CLI 在内部都是使用 Uvicorn 这个高性能的 ASGI 服务器来运行你的应用fastapi dev会自动查找项目中的 FastAPI 应用并启动开发服务器等同于uvicorn main:app --reload3.2.6 使用uvicorn 启动上面通过命令行的方式启动是不是有点不方便还可以直接编写main函数通过uvicorn 来启动from fastapi import FastAPI app FastAPI() app.get(/) async def root(): return {message: Hello World} if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)四、FastAPI 接口请求参数使用4.1 接口参数4.1.1 接口参数说明在实际开发中为了响应页面的数据要求同一段接口逻辑需要根据参数不同返回不同的数据比如在下面这个图中根据不同的书籍ID查询不同的图书信息ID就是动态传入的接口也需要返回不同的书籍信息参数就是客户端发送请求时附带的额外信息和指令参数的作用是让同一个接口能根据不同的输入返回不同的输出实现动态交互4.1.2 接口参数分类参数可以分为下面3类4.2 路径参数路径参数是 URL 路径中的动态部分使用花括号{}声明。FastAPI 会自动将路径参数传递给路径操作函数并根据类型注解进行数据转换和校验。使用 Python 格式化字符串的语法声明路径参数下面这段代码展示了如何使用路径参数from fastapi import FastAPI app FastAPI() # {item_id} 是路径参数 app.get(/items/{item_id}) async def read_item(item_id: int): return {item_id: item_id}4.2.1 路径参数类型除了int你还可以使用其他标准 Python 类型类型说明URL 示例str字符串默认类型/items/fooint整数/items/5float浮点数/items/5.5bool布尔值/items/trueuuid.UUIDUUID/items/3fa85f64-5717-4562-b3fc-2c963f66afa64.2.2 路径顺序当多个路由可能匹配同一个 URL 时定义的顺序决定了匹配结果。FastAPI 按照路由定义的顺序依次匹配第一个匹配的路由将被执行。如下代码from fastapi import FastAPI app FastAPI() # 必须在 /users/{user_id} 之前定义 app.get(/users/me) async def read_user_me(): 获取当前用户信息 return {user_id: the current user} app.get(/users/{user_id}) async def read_user(user_id: str): 根据 ID 获取用户信息 return {user_id: user_id}如果把/users/me放在/users/{user_id}之后那么访问/users/me时FastAPI 会认为me是user_id的值从而匹配到错误的函数固定路径的路由一定要放在动态路径参数的路由之前否则动态参数会把固定路径的值吞掉4.2.3 预设值的路径参数Enum当你需要限制路径参数只能是几个固定值时可以使用 Python 的Enum类型如下代码from enum import Enum from fastapi import FastAPI # 创建枚举类继承 str 和 Enum class ModelName(str, Enum): alis alis evy evy lenet lenet app FastAPI() app.get(/models/{model_name}) async def get_model(model_name: ModelName): # 可以与枚举成员比较 if model_name is ModelName.alis: return {model_name: model_name, message: Deep Learning FTW!} if model_name.value lenet: return {model_name: model_name, message: LeCNN all the images} return {model_name: model_name, message: Have some residuals}启动服务器后测试一下接口如果传入非预设值如/models/foobarFastAPI 会返回校验错误提示可选值为 alis、evy、lenet枚举类的要点要点说明继承str让 API 文档将值类型识别为字符串确保正确渲染继承Enum创建枚举类型限制可选值model_name.value获取枚举成员的实际值如alexnet4.2.4 包含路径的路径参数当你需要路径参数本身包含路径如文件路径时使用 Starlette 的路径转换器参考下面的代码from fastapi import FastAPI app FastAPI() # :path 表示该参数可以匹配包含斜杠的路径 app.get(/files/{file_path:path}) async def read_file(file_path: str): return {file_path: file_path}注意注意 URL 中 /files/ 和 /home/ 之间会出现双斜杠 //这是正常的因为路径参数以 / 开头4.3 查询参数查询参数是 URL 中?之后、以分隔的键值对。当函数参数不是路径参数也不是请求体时FastAPI 会将其自动解释为查询参数。4.3.1 基本用法声明查询参数只需要在函数参数中添加类型注解和默认值如下这段代码from fastapi import FastAPI app FastAPI() # skip 和 limit 是查询参数有默认值 fake_items_db [{item_name: Foo}, {item_name: Bar}, {item_name: Baz}] app.get(/items/) async def read_item(skip: int 0, limit: int 10): # 模拟分页查询 return fake_items_db[skip : skip limit]针对代码中的参数有下面的访问形式URLskip 的值limit 的值说明/items/010使用默认值/items/?skip202010skip 使用传入值limit 使用默认值/items/skip20limit5205两个参数都使用传入值其他可选参数将默认值设为None即可声明可选的查询参数在下面的接口中这里q: str | None None表示q可以是字符串或None默认值为None。from fastapi import FastAPI app FastAPI() app.get(/items/{item_id}) async def read_item(item_id: str, q: str | None None): # item_id 是路径参数必填q 是查询参数可选 if q: return {item_id: item_id, q: q} return {item_id: item_id}FastAPI 通过默认值 None判断参数是否必填而不是通过类型注解str | None。类型注解主要帮助编辑器提供更好的支持。4.3.2 必选查询参数不设置默认值的查询参数即为必选参数from fastapi import FastAPI app FastAPI() app.get(/items/{item_id}) async def read_item(item_id: str, needy: str): # needy 没有默认值是必选查询参数 return {item_id: item_id, needy: needy}在上面的代码中如果接口参数中不传needy调用将会报下面的错误4.3.3 参数混合使用混合使用必选、有默认值和可选参数可以在同一个函数中混合使用不同类型的查询参数from fastapi import FastAPI app FastAPI() app.get(/items/{item_id}) async def read_item( item_id: str, # 路径参数必填 needy: str, # 必选查询参数 skip: int 0, # 有默认值的查询参数 limit: int | None None, # 可选查询参数 ): item {item_id: item_id, needy: needy, skip: skip} if limit: item.update({limit: limit}) return item4.4 查询参数校验FastAPI 允许开发者为查询参数声明额外的校验规则和元数据例如字符串长度限制、正则匹配等。通过Query和Annotated你可以在不改变函数逻辑的情况下增强参数校验。4.4.1 参数基本校验以下示例为查询参数q添加最大长度限制from typing import Annotated from fastapi import FastAPI, Query app FastAPI() app.get(/items/) async def read_items( # 使用 Annotated Query 添加校验 q: Annotated[str | None, Query(max_length5)] None, ): results {items: [{item_id: Foo}, {item_id: Bar}]} if q: results.update({q: q}) return results访问接口时当我输入的参数长度超过5的时候报下面的错误参数说明部分说明Annotated[str | None, ...]类型注解表示q可以是字符串或 NoneQuery(max_length50)校验规则q的最大长度为 50 个字符None默认值使参数变为可选补充FastAPI 推荐使用Annotated方式声明校验而非将Query作为默认值。因为Annotated方式下函数的默认值就是真正的默认值更符合 Python 的直觉且编辑器和类型检查工具支持更好。4.4.2 同时添加更多参数校验可以同时添加多种校验规则from typing import Annotated from fastapi import FastAPI, Query app FastAPI() app.get(/items/) async def read_items( # 同时限制最小长度、最大长度和正则表达式 q: Annotated[str | None, Query(min_length3, max_length50, pattern^fixedquery$)] None, ): results {items: [{item_id: Foo}, {item_id: Bar}]} if q: results.update({q: q}) return results正则表达式^fixedquery$的含义^-- 必须以接下来的字符开头fixedquery-- 值必须精确等于fixedquery$-- 到此结束后面不能有其他字符4.4.3 带默认值的校验你可以为查询参数同时设置默认值和校验规则from typing import Annotated from fastapi import FastAPI, Query app FastAPI() app.get(/items/) async def read_items( # 默认值为 fixedquery同时要求最小长度为 3 q: Annotated[str, Query(min_length3)] fixedquery, ): results {items: [{item_id: Foo}, {item_id: Bar}]} if q: results.update({q: q}) return results注意任何类型的默认值包括非None值都会让参数变为可选。没有默认值也没有Query(default...)的参数是必填的。4.4.4 必填参数使用Query时如果不声明默认值参数就是必填的from typing import Annotated from fastapi import FastAPI, Query app FastAPI() app.get(/items/) async def read_items( # 没有 None所以 q 是必填参数 q: Annotated[str, Query(min_length3)], ): results {items: [{item_id: Foo}, {item_id: Bar}]} results.update({q: q}) return results有时你需要客户端必须传值但值可以是Nonefrom typing import Annotated from fastapi import FastAPI, Query app FastAPI() app.get(/items/) async def read_items( # 客户端必须提供 q 参数但值可以是 None q: Annotated[str | None, Query(min_length3)], ): results {items: [{item_id: Foo}, {item_id: Bar}]} if q: results.update({q: q}) return results4.5 路径参数值校验与查询参数使用Query添加校验的方式相同你可以使用Path为路径参数声明数值校验和元数据。4.5.1 Path导入从fastapi导入Path从typing导入Annotated路径参数总是必填的因为它必须是 URL 路径的一部分。即使你为其声明了默认值它仍然会作为必填参数处理from typing import Annotated from fastapi import FastAPI, Path app FastAPI() app.get(/items/{item_id}) async def read_items( # 为路径参数添加元数据和校验 item_id: Annotated[int, Path(title商品ID, description要获取的商品ID, ge1)], ): return {item_id: item_id}4.5.2 Path 校验参数说明Path和Query都支持以下数值校验参数参数含义英文来源gt大于greaterthange大于等于greater than orequallt小于lessthanle小于等于less than orequal4.5.3 数值组合校验在下面的案例中限定了输入参数的取值范围from typing import Annotated from fastapi import FastAPI, Path, Query app FastAPI() app.get(/items/{item_id}) async def read_items( # item_id 必须 0 且 1000 item_id: Annotated[int, Path(gt0, le1000)], # 查询参数也可以用数值校验 size: Annotated[float, Query(gt0, lt10.5)] 5.0, ): return {item_id: item_id, size: size}4.5.4 路径参数与查询参数混合使用当你同时使用Path和Query时使用Annotated可以避免参数顺序的问题from typing import Annotated from fastapi import FastAPI, Path, Query app FastAPI() app.get(/items/{item_id}) async def read_items( # 使用 Annotated 后参数顺序无关紧要 item_id: Annotated[int, Path(title商品ID, ge1, le1000)], q: Annotated[str | None, Query(max_length50)] None, ): results {item_id: item_id} if q: results.update({q: q}) return results4.5.5 小结路径参数数值校验的核心要点使用AnnotatedPath声明路径参数的校验规则数值校验gt大于、ge大于等于、lt小于、le小于等于Path和Query共享相同的校验参数包括字符串校验min_length等和元数据title、description等路径参数始终是必填的无论是否声明默认值4.6 请求体参数4.6.1 什么是请求体参数请求体是客户端发送给 API 的数据。当你需要从客户端接收 JSON 数据时使用请求体来传递。FastAPI 使用 Pydantic 模型来声明请求体的结构自动完成数据校验、转换和文档生成。4.6.2 请求体与查询参数的区别两者之间的主要区别如下数据传递方式位置适用场景HTTP 方法路径参数URL 路径/items/5标识资源GET、PUT、DELETE 等查询参数URL 中?keyvalue筛选、分页等可选参数主要是 GET请求体请求的 JSON 数据提交复杂数据POST、PUT、PATCH发送数据应使用 POST最常见、PUT、DELETE 或 PATCH。虽然 FastAPI 技术上支持 GET 请求携带请求体但这不符合 HTTP 规范Swagger UI 也不会为 GET 请求显示请求体文档。4.6.3 使用 Pydantic 模型声明请求体导入 BaseModel定义一个继承BaseModel的类使用 Python 标准类型声明所有属性from fastapi import FastAPI from pydantic import BaseModel app FastAPI() # 定义请求体数据模型 class Item(BaseModel): name: str # 必填商品名称 description: str | None None # 可选商品描述 price: float # 必填商品价格 tax: float | None None # 可选税费 class User(BaseModel): username: str password: str app.post(/register) async def create_item(user: User): return user运行服务接口调用看下效果4.6.4 请求体参数-类型注解Field导入pydantic 的Field 函数在导入的模块中增加 Field如下代码from fastapi import FastAPI from pydantic import BaseModel,Field app FastAPI() # 定义请求体数据模型 class Item(BaseModel): name: str # 必填商品名称 description: str | None None # 可选商品描述 price: float # 必填商品价格 tax: float | None None # 可选税费 class User(BaseModel): username: strField(default张三,min_length2,max_length10,description用户名长度必须是2到10之间) password: strField(min_length5,max_length20) app.post(/register) async def create_item(user: User): return user运行下看效果如果不符合参数要求将会报下面的额错误4.6.5 请求体 路径参数 查询参数混合可以将三者同时使用如下代码from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class Item(BaseModel): name: str description: str | None None price: float tax: float | None None # 同时使用路径参数、查询参数和请求体 app.put(/items/{item_id}) async def update_item(item_id: int, item: Item, q: str | None None): result {item_id: item_id, **item.model_dump()} if q: result.update({q: q}) return resultFastAPI 的完整参数识别规则识别条件参数来源参数名在路径的{}中声明路径参数参数是单一类型int、str、bool等查询参数参数类型是 Pydantic 模型请求体4.6.6 小结请求体的核心要点使用 Pydantic 的BaseModel定义请求体结构有默认值的字段可选没有默认值的字段必填请求体可以与路径参数和查询参数同时使用FastAPI 自动完成数据校验、类型转换和文档生成Pydantic v2 使用model_dump()进行序列化五、写在最后本文通过案例操作演示了FastAPI从环境搭建到查询参数的使用希望对看到的同学有用哦本文到此结束感谢观看。