news 2026/10/16 2:56:23

Python FastAPI 介绍以及常用方法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python FastAPI 介绍以及常用方法

FastAPI 完整使用指南 + 常用示例

FastAPI 是一个高性能、工程化的 Python Web API 框架,基于标准 Python 类型提示开发,底层依赖Starlette(ASGI 异步底层)和Pydantic(自动数据校验),原生支持自动生成交互式 API 文档,性能接近 Go/Node.js,是当前 Python 后端 RESTful API 开发的主流方案。


前置安装

需要安装 FastAPI 本体 + ASGI 运行服务器 Uvicorn:

pipinstallfastapi uvicorn

一、最小入门示例

创建main.py文件:

fromfastapiimportFastAPI# 创建 FastAPI 应用实例app=FastAPI(title="示例接口",version="1.0")# 定义 GET 接口,路径为 /@app.get("/")defhello():return{"message":"Hello FastAPI"}

拆解 @app.get(“/”)

  • app:你创建的 FastAPI() 实例。
  • .get:表示这个路由只响应 HTTP GET 方法。FastAPI 还提供 .post、.put、.delete、.patch 等方法。
  • “/”:URL 路径。“/” 表示根路径,即 http://127.0.0.1:8000/。

所以 @app.get(“/”) 的意思是:当有人用 GET 方法访问根路径 / 时,执行下面这个函数来处理请求。

具体来说:

  • 函数名 hello 本身不重要,可以随意取,关键是它被上面的装饰器绑定到了 GET /。
  • 函数返回值会由 FastAPI 自动转换成 JSON 响应。这里返回一个字典,客户端收到的是 {“message”: “Hello World”}。
  • 用 async def 定义表示这是一个协程函数,FastAPI 会在事件循环中异步执行它,适合里面有 await(比如查数据库、调外部 API)的场景。如果函数里全是同步代码,用普通的 def 也完全可以,FastAPI 会在线程池中运行它,不会阻塞事件循环。

启动服务

在终端执行命令(main是文件名,app是代码中的实例名):

uvicorn main:app--reload
  • --reload:开发环境热重载,代码修改后自动重启,生产环境禁用。
  • 默认地址:[http://127.0.0.1:8000](http://127.0.0.1:8000)

核心特性:自动生成接口文档

启动后直接访问以下地址,可在线调试接口:

  • Swagger UI 交互式文档:[http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)
  • ReDoc 文档:[http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc)

二、路径参数(Path Parameters)

把参数写在 URL 路径中,支持自动类型校验和转换。

fromfastapiimportFastAPI app=FastAPI()# 路径参数 item_id,自动约束为 int 类型@app.get("/items/{item_id}")defget_item(item_id:int):return{"item_id":item_id,"name":f"商品{item_id}"}
  • 装饰器声明:这个函数负责处理 GET /items/{item_id}。
  • 函数签名声明:路径里的 {item_id} 会自动作为参数 item_id 传进来,并转换为 int;查询参数 ?q=xxx 对应 q。
  • 函数体:拿到这些参数后做业务处理,返回值就是响应体。

简单说,装饰器负责“什么时候调用这个函数”,函数负责“拿到参数后做什么、返回什么”。
说明

  1. 传入item_id=123自动识别为整数;传入非数字字符串会直接返回 422 校验错误,无需手动判断。
  2. 支持枚举限定参数取值范围:
fromenumimportEnumfromfastapiimportFastAPI app=FastAPI()classCategory(str,Enum):food="food"book="book"electronic="electronic"@app.get("/category/{cate}")defget_category(cate:Category):return{"category":cate.value}

只能传入food/book/electronic三个值,其他值自动校验失败。


三、查询参数(Query Parameters)

URL 中?后面的参数,直接在函数中定义即可自动解析。

fromenumimportEnumfromfastapiimportFastAPI app=FastAPI()@app.get("/goods/")defget_goods(skip:int=0,limit:int=10,keyword:str|None=None):""" 商品列表接口 - skip: 起始偏移量,默认0 - limit: 每页数量,默认10 - keyword: 搜索关键词,可选参数 """return{"skip":skip,"limit":limit,"keyword":keyword}http://127.0.0.1:8000/goods/?skip=1&limit=2&keyword=3

特点

  • 有默认值的参数为可选参数,无默认值则为必填。
  • 布尔类型自动转换:?active=true、?active=1、?active=True都会识别为True。

四、路径参数和查询参数的本质区别

路径参数和查询参数的本质区别,可以归结为一句话:路径参数是"资源标识"的一部分,查询参数是"对资源的附加说明"。这个区别决定了它们在路由匹配、语义、设计上的不同行为。

从 HTTP 和 URL 的角度看

一个 URL 的结构是:

http://host/path?query#fragment ↑ ↑ 路径 查询字符串
  • 路径(path)在?之前,用来定位服务器上的某个资源。/items/1和/items/2在 HTTP 看来是两个完全不同的 URL。
  • 查询字符串(query string)在?之后,用来给这个资源附加参数。/items?id=1和/items?id=2的路径相同,只是查询条件不同。

所以本质区别首先是:参数是否参与了"资源定位"。路径参数改变了资源地址本身,查询参数没有。

从路由匹配的角度看

FastAPI(以及大多数 Web 框架)的路由匹配只看路径部分,不看查询字符串。

@app.get("/items/{item_id}")# 匹配 /items/1、/items/2、/items/abcasyncdefread_item(item_id:int):...@app.get("/items/")# 匹配 /items/、/items/?id=1、/items/?id=2asyncdefread_items(id:int):...
  • 路径参数是路由模板的一部分,框架用它来区分不同路由。/items/{item_id}和/users/{user_id}能匹配到不同函数。
  • 查询参数不参与路由匹配。/items/?id=1和/items/?name=book命中同一个函数,函数再决定读取哪些查询参数。

这是最实际的区别:路径参数决定了"调用哪个函数",查询参数只是"传给这个函数的输入"。

从语义设计的角度看

这来自 REST 的资源模型:

  • 路径参数标识资源。/items/1表示"id 为 1 的那个 item",它是一个唯一资源。删掉它、修改它,操作对象是明确的。
  • 查询参数描述资源的视图。/items?category=book&page=2表示"items 集合中,category 为 book 的第 2 页"。它不改变"items 集合"这个资源本身,只是筛选、排序、分页。

一个常用的判断方法:如果去掉这个参数,URL 还指向同一个东西吗?

  • /items/1去掉1变成/items/,指向的是集合,不是同一个资源 → 应该用路径参数。
  • /items?page=2去掉page=2变成/items,还是同一个集合,只是回到第一页 → 应该用查询参数。

从代码行为的角度看

维度路径参数查询参数
在 URL 中的位置?之前?之后
是否影响路由匹配是否
是否必填通常必填(否则路由不匹配)可有默认值,可选
函数中如何声明参数名与{name}一致参数名不出现在路径里
典型用途定位单个资源过滤、排序、分页、搜索

一个容易混淆的点

有人会问:/items/1和/items?id=1结果可能一样,为什么不能随便用?

因为它们的语义和扩展性不同:

  • /items/1是"这个资源"。如果你想同时支持/items/1/reviews(某个 item 的评论),路径参数天然支持层级嵌套。
  • /items?id=1是"从集合里筛出 id=1"。这种写法下,/items/reviews就没法自然表达"某个 item 的评论"了。

所以本质区别不只是技术实现,更是URL 设计模型:路径表达"是什么资源",查询表达"要什么样的资源"。

总结

路径参数是资源地址的组成部分,决定路由和资源身份;查询参数是附加在地址后的选项,用来筛选或配置这个资源。前者改变"你访问的是谁",后者改变"你希望怎么看它"。

五、请求体:结合 Pydantic 自动校验

基本用法

FastAPI 的 POST 请求主要用于创建资源或提交数据,数据通常放在请求体(request body)中。
POST/PUT 请求的 JSON 体,配合 Pydantic 的BaseModel实现自动解析 + 数据校验。

# 定义请求体模型fromfastapiimportFastAPIfrompydanticimportBaseModel app=FastAPI()classItem(BaseModel):name:strprice:floattax:float|None=None@app.post("/items/")asyncdefcreate_item(item:Item):return{"name":item.name,"price":item.price,"tax":item.tax}

客户端发送

POST/items/Content-Type:application/json{"name":"book","price":12.5,"tax":1.2}

说明

  • @app.post(“/items/”) 把这个函数绑定到 POST /items/。
  • 参数 item: Item 声明为 Pydantic 模型,FastAPI 会自动把请求体 JSON 解析并校验成 Item 对象。
  • 返回值自动序列化为 JSON 响应。
  • FastAPI 自动从请求中读取 JSON,转换为Item对象,并执行校验。
  • 校验不通过时自动返回 422 错误和详细的错误信息,无需手动写 if 判断。

请求体参数与路径/查询参数混合

POST 路由里可以同时有路径参数、查询参数和请求体:

@app.post("/items/{item_id}")asyncdefupdate_item(item_id:int,# 路径参数q:str|None=None,# 查询参数item:Item=None,# 请求体):return{"item_id":item_id,"q":q,"item":item}

请求 POST /items/5?q=test,body 为 JSON 时:

  • item_id 从路径取,q 从查询串取,item 从 body 取。
  • FastAPI 根据参数类型和位置自动判断来源:Pydantic 模型 → body;简单类型且名字在路径里 → 路径;其余简单类型 → 查询。

请求体的多种形式

单个模型:最常见,直接声明一个 Pydantic 模型参数。

多个模型:如果函数里有多个 Pydantic 模型参数,FastAPI 会把它们合并成一个 JSON 对象,每个模型作为顶层的一个键:

classUser(BaseModel):username:strfull_name:str|None=None@app.post("/users/")asyncdefcreate_user(user:User,item:Item):return{"user":user,"item":item}

期望的 body 是:

{ "user": {"username": "alice"}, "item": {"name": "book", "price": 12.5} }

用 Body 显式声明单值:如果只想接收一个简单值作为 body(而不是查询参数),用 Body:

fromfastapiimportBody@app.post("/items/")asyncdefcreate_item(name:str=Body(...),price:float=Body(...)):return{"name":name,"price":price}

此时 body 是 {“name”: “book”, “price”: 12.5},而不是查询串。

嵌入单个模型:默认单个模型参数期望 body 就是模型本身;加 Body(embed=True) 可以要求它嵌在一个键下:
python

@app.post("/items/") async def create_item(item: Item = Body(embed=True)): ...

期望 body 变成 {“item”: {“name”: “book”, “price”: 12.5}}。

数据校验

Pydantic 模型负责校验,字段缺失、类型不符都会返回 422 Unprocessable Entity,并附带详细错误信息:

{ "detail": [ { "type": "missing", "loc": ["body", "price"], "msg": "Field required" } ] }

可以在模型里用 Field 加约束:

frompydanticimportBaseModel,FieldclassItem(BaseModel):name:str=Field(min_length=1,max_length=100)price:float=Field(gt=0)

状态码与响应模型

POST 创建资源通常返回 201 Created:

fromfastapiimportstatus@app.post("/items/",status_code=status.HTTP_201_CREATED)asyncdefcreate_item(item:Item):returnitem

用 response_model 控制响应体的字段(比如隐藏敏感字段):

classItemOut(BaseModel):name:strprice:float@app.post("/items/",response_model=ItemOut)asyncdefcreate_item(item:Item):returnitem

表单与文件上传

如果前端提交的是表单而不是 JSON,用 Form:

fromfastapiimportForm@app.post("/login/")asyncdeflogin(username:str=Form(...),password:str=Form(...)):return{"username":username}

上传文件用 UploadFile:

fromfastapiimportUploadFile,File@app.post("/upload/")asyncdefupload(file:UploadFile=File(...)):content=awaitfile.read()return{"filename":file.filename,"size":len(content)}

注意:表单和文件需要 python-multipart,安装 fastapi[standard] 时已包含。


六、参数精细化校验:Path / Query

针对路径参数和查询参数,使用Path和Query实现更精细的校验规则(和 Pydantic 的Field用法一致)。

fromfastapiimportFastAPI,Path,Query app=FastAPI()@app.get("/items/{item_id}")defread_item(# 路径参数校验:必须大于0item_id:int=Path(gt=0,description="商品ID,必须大于0"),# 查询参数校验:字符串长度1-50,可选q:str|None=Query(default=None,min_length=1,max_length=50)):return{"item_id":item_id,"q":q}

补充:请求头 / Cookie 参数

fromfastapiimportHeader,Cookie@app.get("/header/")defget_header(user_agent:str|None=Header(default=None),# 获取请求头 User-Agentsession_id:str|None=Cookie(default=None)# 获取 Cookie 中的 session_id):return{"user_agent":user_agent,"session_id":session_id}

七、响应模型:统一格式 + 字段过滤

通过response_model指定返回数据的结构,自动过滤敏感字段(比如密码)、统一返回格式。

fromfastapiimportFastAPIfrompydanticimportBaseModel app=FastAPI()# 请求模型:包含密码classUserCreate(BaseModel):username:strpassword:stremail:str# 响应模型:不返回密码classUserResponse(BaseModel):username:stremail:str@app.post("/users/",response_model=UserResponse)defcreate_user(user:UserCreate):# 模拟数据库保存,返回完整用户对象(含密码)user_in_db=user.model_dump()# 自动按照 UserResponse 过滤字段,密码不会返回给前端returnuser_in_db

即使函数返回了包含password的字典,FastAPI 也会自动按照response_model裁剪字段,保证数据安全。


八、HTTP 状态码与异常处理

自定义响应状态码

通过status_code参数设置接口成功时的状态码,比如 201(创建成功)、204(无内容)。

抛出 HTTP 异常

使用HTTPException主动抛出业务异常,返回标准错误响应。

fromfastapiimportFastAPI,HTTPException app=FastAPI()@app.get("/users/{user_id}")defget_user(user_id:int):ifuser_id<=0:# 抛出 400 错误raiseHTTPException(status_code=400,detail="用户ID不合法")ifuser_id>100:# 抛出 404 错误raiseHTTPException(status_code=404,detail="用户不存在")return{"user_id":user_id,"name":"测试用户"}

九、依赖注入:逻辑复用(Depends)

FastAPI 的核心特性之一,用于抽离公共逻辑(分页、鉴权、参数解析等),在多个接口中复用。

示例1:通用分页依赖

把一个可复用的函数挂到路由上,让它自动解析参数并把结果传给接口函数。

fromfastapiimportFastAPI,Depends app=FastAPI()# 定义分页依赖函数defget_pagination(skip:int=0,limit:int=10):return{"skip":skip,"limit":limit}# 接口中注入依赖,自动接收分页参数@app.get("/goods/")defget_goods(pagination:dict=Depends(get_pagination)):returnpagination

get_pagination 是一个普通函数,定义了两个查询参数的默认值:

  • skip 默认 0
  • limit 默认 10

它返回一个字典。这个函数本身跟路由无关,是一个可复用的"依赖"。

  • Depends(get_pagination) 告诉 FastAPI:在处理这个请求前,先调用 get_pagination。
  • pagination: dict = … 表示把 get_pagination 的返回值(那个字典)赋给 pagination。
  • 所以 get_goods 拿到的 pagination 就是 {“skip”: …, “limit”: …}。

请求时发生什么

访问 GET /goods/?skip=5&limit=20 时:

  • FastAPI 看到 Depends(get_pagination),去调用 get_pagination。
  • get_pagination 的参数 skip、limit 是查询参数,FastAPI 从 URL 里取值:skip=5、limit=20。
  • 函数返回 {“skip”: 5, “limit”: 20}。
  • 这个字典作为 pagination 传入 get_goods。
  • get_goods 直接返回它,响应就是:
    {“skip”: 5, “limit”: 20}

对比:不用依赖注入的写法

@app.get("/goods/")defget_goods(skip:int=0,limit:int=10):return{"skip":skip,"limit":limit}

效果完全一样,但如果十个接口都要分页,就得写十遍。用 Depends 只需写一次 get_pagination,然后每个接口写 pagination: dict = Depends(get_pagination) 即可。

Depends(get_pagination) 让 FastAPI 在调用接口前先执行 get_pagination,把它解析出的分页参数自动注入到 get_goods 里。这样分页逻辑写一次,所有接口都能复用。

示例2:用户鉴权依赖(模拟)

defget_current_user(token:str|None=Query(default=None)):ifnottoken:raiseHTTPException(status_code=401,detail="未登录")return{"user_id":1,"username":"admin"}@app.get("/user/info")defuser_info(current_user=Depends(get_current_user)):return{"当前用户":current_user}

十、表单数据与文件上传

1. 表单提交(application/x-www-form-urlencoded)

fromfastapiimportFastAPI,Form app=FastAPI()@app.post("/login/")deflogin(username:str=Form(),password:str=Form()):return{"username":username,"login":True}

2. 文件上传(multipart/form-data)

推荐使用UploadFile,支持大文件异步处理、文件名获取、文件大小等。

fromfastapiimportFastAPI,File,UploadFile app=FastAPI()@app.post("/upload/")defupload_file(file:UploadFile=File()):return{"文件名":file.filename,"文件类型":file.content_type,}

十一、路由拆分:APIRouter 模块化

项目变大后,不能把所有接口写在一个文件里,用APIRouter按模块拆分路由。

新建routers/user.py

fromfastapiimportAPIRouter router=APIRouter(prefix="/users",tags=["用户模块"])@router.get("/{user_id}")defget_user(user_id:int):return{"user_id":user_id}@router.post("/")defcreate_user():return{"msg":"创建用户"}

主文件main.py挂载路由

fromfastapiimportFastAPIfromrouters.userimportrouterasuser_router app=FastAPI()# 挂载用户路由app.include_router(user_router)

十二、常用配置:跨域(CORS)

前后端分离项目必须配置跨域,使用内置中间件实现:

fromfastapiimportFastAPIfromfastapi.middleware.corsimportCORSMiddleware app=FastAPI()# 配置允许的源app.add_middleware(CORSMiddleware,allow_origins=["*"],# 生产环境替换为具体域名allow_credentials=True,allow_methods=["*"],allow_headers=["*"],)

典型应用场景

  1. 前后端分离的后端 RESTful API 开发
  2. 微服务内部接口、数据服务接口
  3. 机器学习模型的 HTTP 服务化封装
  4. 配合 SQLAlchemy / Tortoise-ORM 开发数据库业务系统
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 2:55:48

STM32内部温度传感器详解:原理、公式、代码与校准

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 2:55:43

让旧 Mac 跑上最新 macOS:OCLP 完整路径一次跑通

让旧 Mac 跑上最新 macOS&#xff1a;OCLP 完整路径一次跑通 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher OpenCore Legacy Patcher&#xff08;OCLP&#…

作者头像 李华
网站建设 2026/10/16 2:55:40

倍福TcHmi上位机性能优化:异步子线程ADS通信防卡顿方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 2:54:34

InvokeAI 部署选型指南:本地安装与 Docker 对比

InvokeAI 部署选型指南:本地安装与 Docker 对比 【免费下载链接】InvokeAI Invoke is a leading creative engine for Stable Diffusion models, empowering professionals, artists, and enthusiasts to generate and create visual media using the latest AI-driven technol…

作者头像 李华