当前位置: 首页 > news >正文

FastAPI中GET、POST、PUT、DELETE到底怎么选?一篇讲透!

前言:你是不是也经常懵?

刚开始学FastAPI的时候,我每次写接口都要纠结半天:

  • 查数据用GET还是POST?
  • 更新数据用PUT还是POST?它们有啥区别?
  • 删除资源到底用DELETE还是POST?

相信很多同学都有同样的困惑。网上很多教程一上来就贴代码,讲完语法却不告诉你什么场景该用什么。这篇文章换个思路——先讲场景,再上代码,最后一张表总结,看完你就彻底清楚了。


一、核心概念:四个接口到底什么关系?

GET、POST、PUT、DELETE是四种HTTP方法,对应数据的增删改查(CRUD)操作。

打个比方——把你的服务器想象成一个仓库管理员

HTTP方法类比回答的问题对应CRUD
GET去仓库查货"这个货架上有多少件货?"Read(查)
POST往仓库存新货"给我新增一个货架,放这批货"Create(增)
PUT把仓库的货整个换掉"把3号货架上的货全部清空,换成这批新货"Update(改)
DELETE从仓库销毁货物"把3号货架上的货扔掉"Delete(删)

一句话总结:

GET只读不改,POST新增创建,PUT整体替换,DELETE删除资源。

下面逐个拆解:


二、GET接口:只管查,不管改

什么时候用GET?
  • 获取资源列表或详情
  • 搜索、筛选、分页查询
  • 不修改服务器上的任何数据

GET请求的核心特征是安全且幂等——安全意味着不产生副作用,幂等意味着调用1次和调用100次结果一样。

完整代码示例
from fastapi import FastAPI, Query, HTTPException from pydantic import BaseModel app = FastAPI() # 模拟数据库 fake_users = { 1: {"id": 1, "name": "张三", "age": 25, "email": "zhangsan@test.com"}, 2: {"id": 2, "name": "李四", "age": 30, "email": "lisi@test.com"}, } class UserOut(BaseModel): id: int name: str age: int email: str # 场景1:获取所有用户(支持分页查询) @app.get("/users/", response_model=list[UserOut]) async def get_users(skip: int = 0, limit: int = Query(10, le=100)): """查询用户列表,skip和limit通过URL查询参数传递""" users = list(fake_users.values()) return users[skip : skip + limit] # 场景2:获取单个用户详情 @app.get("/users/{user_id}", response_model=UserOut) async def get_user(user_id: int): """通过路径参数获取指定用户""" if user_id not in fake_users: raise HTTPException(status_code=404, detail="用户不存在") return fake_users[user_id] # 场景3:按关键词搜索用户 @app.get("/users/search/", response_model=list[UserOut]) async def search_users( keyword: str = Query(..., min_length=1, description="搜索关键词"), max_age: int = Query(default=100, le=150, description="年龄上限"), ): """通过查询参数搜索用户""" results = [ u for u in fake_users.values() if keyword in u["name"] and u["age"] <= max_age ] return results
关键点
特征说明
参数位置路径参数/users/{id}或查询参数?skip=0&limit=10
请求体不能有请求体(GET请求不携带body)
幂等性幂等——多次调用结果相同
安全性安全——不修改服务器数据
缓存浏览器/CDN可以缓存GET响应
使用场景查列表、查详情、搜索、筛选

注意路由顺序/users/search/必须定义在/users/{user_id}之前,否则FastAPI会把search当成user_id来匹配。

三、POST接口:创建新东西

什么时候用POST?
  • 创建新资源(注册用户、新增文章、提交订单)
  • 提交表单数据
  • 执行一个非幂等的操作(同样的请求提交两次会创建两条记录)

POST的核心特征是不幂等——提交两次相同的数据,会创建两个资源。

完整代码示例
from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field app = FastAPI() fake_users = { 1: {"id": 1, "name": "张三", "age": 25, "email": "zhangsan@test.com"}, } next_id = 2 # 请求模型——客户端提交的数据 class UserCreate(BaseModel): name: str = Field(..., min_length=1, max_length=50, description="用户名") age: int = Field(..., ge=0, le=150, description="年龄") email: str = Field(..., description="邮箱地址") # 响应模型——返回给客户端的数据 class UserOut(BaseModel): id: int name: str age: int email: str @app.post("/users/", response_model=UserOut, status_code=201) async def create_user(user: UserCreate): """创建新用户,数据通过请求体(body)传递""" global next_id # 检查邮箱是否重复 for existing in fake_users.values(): if existing["email"] == user.email: raise HTTPException(status_code=400, detail="邮箱已被注册") # 存入"数据库" new_user = { "id": next_id, "name": user.name, "age": user.age, "email": user.email, } fake_users[next_id] = new_user next_id += 1 return new_user
关键点
特征说明
参数位置请求体(body),用Pydantic模型接收
幂等性不幂等——重复提交会创建多个资源
请求体必须有请求体(通常JSON格式)
安全性不安全——会修改服务器数据
使用场景创建资源、提交表单、上传文件

四、PUT接口:整体替换

什么时候用PUT?
  • 完整更新一个资源(把旧数据整体替换成新数据)
  • 创建一个已知ID的资源(如果不存在就创建,存在就覆盖)

PUT的核心特征是幂等——对同一个资源用相同的数据调用1次和100次,最终状态完全一样。因为PUT是"整体替换",替换成同样的内容,结果不变。

PUT vs POST:最容易搞混的这对
对比维度POSTPUT
语义创建新资源替换/更新已有资源
幂等性不幂等(调两次创建两条)幂等(调两次结果一样)
谁决定ID服务器决定(服务器分配新ID)客户端决定(URL中指定ID)
典型URLPOST /users/PUT /users/{id}
完整代码示例
from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field app = FastAPI() fake_users = { 1: {"id": 1, "name": "张三", "age": 25, "email": "zhangsan@test.com"}, 2: {"id": 2, "name": "李四", "age": 30, "email": "lisi@test.com"}, } class UserUpdate(BaseModel): """PUT请求模型——所有字段必须提供,因为是整体替换""" name: str = Field(..., min_length=1, max_length=50, description="用户名") age: int = Field(..., ge=0, le=150, description="年龄") email: str = Field(..., description="邮箱地址") class UserOut(BaseModel): id: int name: str age: int email: str @app.put("/users/{user_id}", response_model=UserOut) async def update_user(user_id: int, user: UserUpdate): """PUT:整体替换用户信息 客户端必须提供所有字段。 如果某个字段没传,PUT会把它覆盖为None或报错。 """ if user_id not in fake_users: raise HTTPException(status_code=404, detail="用户不存在") # 整体替换——所有字段都用新值覆盖 fake_users[user_id] = { "id": user_id, "name": user.name, "age": user.age, "email": user.email, } return fake_users[user_id]
PUT的"整体替换"到底是什么意思?

假设数据库中有个用户:

{"id": 1, "name": "张三", "age": 25, "email": "zhangsan@test.com"}

你只想改名字,用PUT发送:

{"name": "张三丰"}

结果:age和email会被覆盖掉!因为PUT的语义是"整体替换",你没提供的字段会被清空。

如果你只想改名字,应该提供所有字段:

{"name": "张三丰", "age": 25, "email": "zhangsan@test.com"}

补充:如果你只想改一个字段、不想传所有字段,那应该用PATCH方法(局部更新)。FastAPI中用@app.patch(),请求模型中所有字段设为Optional。本文主要讲四种核心方法,PATCH道理类似。

关键点
特征说明
参数位置路径参数指定资源 + 请求体提供新数据
幂等性幂等——相同数据多次调用结果一致
请求体必须有请求体(完整的资源数据)
核心语义整体替换,客户端必须提供所有字段
使用场景完整更新资源、Upsert(存在则更新,不存在则创建)

五、DELETE接口:删东西

什么时候用DELETE?
  • 删除指定资源(删除用户、删除文章、删除订单)
  • 取消订阅、注销账号

DELETE的核心特征是幂等——删除同一个资源,不管调1次还是100次,最终状态都是"已删除"。

完整代码示例
from fastapi import FastAPI, HTTPException from pydantic import BaseModel app = FastAPI() fake_users = { 1: {"id": 1, "name": "张三", "age": 25, "email": "zhangsan@test.com"}, 2: {"id": 2, "name": "李四", "age": 30, "email": "lisi@test.com"}, } class DeleteResult(BaseModel): success: bool message: str deleted_id: int @app.delete("/users/{user_id}", response_model=DeleteResult) async def delete_user(user_id: int): """通过路径参数指定要删除的用户""" if user_id not in fake_users: raise HTTPException(status_code=404, detail="用户不存在") deleted_user = fake_users.pop(user_id) return DeleteResult( success=True, message=f"用户 {deleted_user['name']} 已删除", deleted_id=user_id, )
关键点
特征说明
参数位置通常用路径参数/users/{id}指定要删除的资源
请求体一般不需要请求体(但FastAPI允许DELETE带body)
幂等性幂等——删除已删除的资源,结果还是"不存在"
安全性不安全——会修改服务器数据
使用场景删除资源、注销、取消

常见疑问:删除操作要不要返回数据?两种做法都可以:返回被删除的资源信息,或者只返回一个状态信息(如上面的DeleteResult)。RESTful规范没有强制要求,团队统一即可。


六、一张表总结:什么时候用什么

维度GETPOSTPUTDELETE
核心用途查询数据创建数据整体更新数据删除数据
对应CRUDRead(查)Create(增)Update(改)Delete(删)
参数位置路径+查询参数请求体(body)路径参数+请求体路径参数
请求体不能有必须有必须有(完整数据)通常不需要
幂等性幂等不幂等幂等幂等
安全性安全(无副作用)不安全不安全不安全
会修改数据不会
谁决定ID不涉及服务器决定客户端指定(URL中)客户端指定(URL中)
典型URLGET /users/{id}POST /users/PUT /users/{id}DELETE /users/{id}
典型状态码200201 Created200200或204 No Content

速记口诀:

GET查不改,POST建不幂等,PUT全替换,DELETE删了算。

七、完整实战:四个接口串起来

下面是一个完整的用户管理接口,四种方法全部用到:

from fastapi import FastAPI, HTTPException, Query from pydantic import BaseModel, Field from typing import Optional app = FastAPI(title="用户管理API") # 模拟数据库 db = { 1: {"id": 1, "name": "张三", "age": 25, "email": "zhangsan@test.com"}, } next_id = 2 # ========== 数据模型 ========== class UserCreate(BaseModel): """POST创建用户的请求模型""" name: str = Field(..., min_length=1, max_length=50) age: int = Field(..., ge=0, le=150) email: str = Field(..., description="邮箱地址") class UserUpdate(BaseModel): """PUT更新用户的请求模型——所有字段必须提供""" name: str = Field(..., min_length=1, max_length=50) age: int = Field(..., ge=0, le=150) email: str = Field(..., description="邮箱地址") class UserOut(BaseModel): """对外响应模型""" id: int name: str age: int email: str class DeleteResult(BaseModel): """删除操作响应模型""" success: bool message: str deleted_id: int # ========== 接口实现 ========== # 1. GET:查询用户列表 @app.get("/users/", response_model=list[UserOut], summary="获取用户列表") async def list_users(skip: int = 0, limit: int = Query(10, le=100)): users = list(db.values()) return users[skip : skip + limit] # 2. GET:查询单个用户 @app.get("/users/{user_id}", response_model=UserOut, summary="获取用户详情") async def get_user(user_id: int): if user_id not in db: raise HTTPException(status_code=404, detail="用户不存在") return db[user_id] # 3. POST:创建新用户 @app.post("/users/", response_model=UserOut, status_code=201, summary="创建用户") async def create_user(user: UserCreate): global next_id for u in db.values(): if u["email"] == user.email: raise HTTPException(status_code=400, detail="邮箱已被注册") new_user = { "id": next_id, "name": user.name, "age": user.age, "email": user.email, } db[next_id] = new_user next_id += 1 return new_user # 4. PUT:整体更新用户 @app.put("/users/{user_id}", response_model=UserOut, summary="更新用户") async def update_user(user_id: int, user: UserUpdate): if user_id not in db: raise HTTPException(status_code=404, detail="用户不存在") db[user_id] = { "id": user_id, "name": user.name, "age": user.age, "email": user.email, } return db[user_id] # 5. DELETE:删除用户 @app.delete("/users/{user_id}", response_model=DeleteResult, summary="删除用户") async def delete_user(user_id: int): if user_id not in db: raise HTTPException(status_code=404, detail="用户不存在") deleted = db.pop(user_id) return DeleteResult( success=True, message=f"用户 {deleted['name']} 已删除", deleted_id=user_id, )

运行方式:

pip install fastapi uvicorn uvicorn main:app --reload # 打开 http://127.0.0.1:8000/docs 即可在Swagger UI中测试 #或者下载Apifox中调式

八、常见踩坑总结

坑1:用POST做查询

有些同学习惯全部用POST,觉得方便。但这破坏了HTTP语义,导致浏览器无法缓存结果、CDN无法缓存、API语义混乱。查询就用GET,创建就用POST,别偷懒。

坑2:搞混PUT和POST

最经典的混淆:你想更新一个用户,却用了POST。

POST /users/1 → 语义上是"在/users/1下创建新资源",不是更新 PUT /users/1 → 语义上是"替换/users/1这个资源",这才是更新

记住:URL中有具体ID + 要修改数据 → 用PUT;URL中无ID + 要创建数据 → 用POST。

坑3:PUT只传了部分字段

# 数据库中:{"id": 1, "name": "张三", "age": 25, "email": "zhangsan@test.com"} # 你只想改名字,用PUT只传了name: PUT /users/1 {"name": "张三丰"} # 结果:age和email被覆盖为None!因为PUT是"整体替换",没传的字段会被清掉

正确做法:PUT请求必须携带所有字段。如果只想改一个字段,用PATCH(@app.patch())。

坑4:路由顺序写反了

# 错误顺序:/users/{user_id} 会先匹配到 /users/search @app.get("/users/{user_id}") async def get_user(user_id: int): ... @app.get("/users/search") async def search_users(): ... # 永远到不了这里! # 正确顺序:固定路径放前面 @app.get("/users/search") async def search_users(): ... @app.get("/users/{user_id}") async def get_user(user_id: int): ...

坑5:把PUT当成"局部更新"

PUT的语义是整体替换,不是局部更新。只想改部分字段应该用PATCH:

class UserPatch(BaseModel): """PATCH请求模型——所有字段可选""" name: Optional[str] = None age: Optional[int] = None email: Optional[str] = None @app.patch("/users/{user_id}", response_model=UserOut) async def patch_user(user_id: int, user: UserPatch): """PATCH:局部更新,只改传了的字段""" if user_id not in db: raise HTTPException(status_code=404, detail="用户不存在") stored = db[user_id] update_data = user.model_dump(exclude_unset=True) stored.update(update_data) return stored

九、总结

回到开头的问题——什么时候用什么接口?

  1. 查数据→ GET,参数放URL,不修改数据
  2. 建数据→ POST,参数放body,服务器分配ID
  3. 改数据→ PUT,参数放URL+body,整体替换
  4. 删数据→ DELETE,参数放URL,删完就没了

记住这个对应关系,90%的场景都能覆盖。剩下的特殊情况(PATCH局部更新)原理类似,举一反三即可。

FastAPI的设计理念就是用类型注解把一切自动化——你声明好模型,验证、过滤、文档全部自动生成。把GET/POST/PUT/DELETE用好,API设计就是一件很享受的事。


如果这篇文章对你有帮助,欢迎点赞收藏!有问题可以在评论区交流,我会一一回复。

http://www.jsqmd.com/news/1379255/

相关文章:

  • C++之std::map 全面详解:底层原理、最佳实践与踩坑指南
  • 小米Pad 5 Windows驱动完整指南:让你的安卓平板变身高效Windows工作站
  • 现代开发者效率工具箱:配置管理、AI助手与规则懒加载实战
  • Godot平滑插件:解决物理帧与渲染帧不同步导致的视觉卡顿
  • 4G全网通SMD贴片天线选型与PCB设计实战指南
  • B站批量取关全攻略:官方工具与脚本技术解析,实现数字断舍离
  • 护肤品牌福来有哪些产品?一文看懂德国药剂师家族的产品矩阵 - 甄选测评馆
  • 统信UOS连接Windows共享打印机:飞腾ARM平台实战指南
  • 高清磁场观察薄膜:原理、应用与实战指南
  • Kimi K3模型开源实战:从API调用到本地部署的完整指南
  • 视频片段自动化提取:从AI识别到批量处理的完整技术方案
  • 2026原木风实木餐桌怎么选:高级耐用品牌推荐 - 优企甄选
  • 太空太阳能电站
  • ROS多节点LIOSAM改造:解决命名冲突与资源竞争
  • 代码块:长文中的‘荧光浮标’!让「关键内容」无损高亮呈现
  • 从零部署本地大模型:Llama.cpp实战指南与性能调优
  • 使用FFmpeg实现音频视频降速处理:从原理到批量脚本全解析
  • 2026年国内适配多场景的玻璃钢脱硫塔厂家** - 甄选测评馆
  • 大模型Function Calling原理与实践:从自然语言到工具调用的AI应用开发
  • Claude Desktop 国内安装教程(Windows,2026)
  • SpringCloud微服务Docker容器化部署实战:从环境配置到编排优化
  • Spring Boot集成Flowable工作流引擎:三注解搞定请假审批流程
  • 杭州参团究竟选哪家更稳妥?2026年杭州旅行社前十纯玩无套路零投诉,家庭暑期出游综合测评参考 - 跟我去旅游
  • WSL2虚拟磁盘迁移指南:释放C盘空间,优化开发环境
  • python的运筹学工业场景模拟第四篇:设备检修项目网络工序,构建关键路径模型,求解最短检修工期,输出关键工序清单。
  • 光被哪一层吸收:分析并优化 a-Si 薄膜太阳能电池
  • 2026年澳洲雇主担保公司性价比哪个好:南石签证靠谱选择!186/482/494长期价值怎么比 - 甄选测评官
  • 代码走查
  • Day1语法:printf 与 scanf 使用
  • Linux系统MySQL 8.0安装配置全指南:从官方仓库部署到安全调优