FastAPI 路由参数详解:三种参数类型与真实场景全掌握
在 FastAPI 开发中,路由参数是客户端与后端交互的核心方式。很多初学者分不清参数该放哪里,其实诀窍很简单:参数的位置决定了它的“语义”——它是“找谁”(路径),是“怎么找”(查询),还是“给什么”(请求体)。
FastAPI 中最常用的路由传参方式共有三种:路径参数(Path Parameters)、查询参数(Query Parameters) 和 请求体参数(Request Body)。下面我们结合真实的业务场景来逐一击破。
一、路径参数(Path Parameters):明确“操作哪个具体资源”
1. 什么是路径参数?
路径参数是直接嵌入在 URL 路径中的动态变量,属于 URL 结构不可分割的一部分。例如 /users/123 中的 123 就是路径参数。
2. 定义方式
在 FastAPI 中,路径参数通过在路径字符串中用 {} 包裹参数名来声明:
python
from fastapi import FastAPI
app = FastAPI()
@app.get("/user/{user_id}")
def get_user(user_id: int):
return {"用户ID": user_id, "message": f"查询用户 {user_id}"}
访问 /user/1001 时,user_id 会自动获取值 1001。
3. 🔥 真实使用场景
路径参数专为资源定位而生,在 RESTful 设计中代表“我要操作哪个唯一对象”。典型场景包括:
-
电商系统的订单详情:
GET /orders/ORD-20260806—— 用户点击“查看订单”,前端直接将订单号拼在路径里。 -
社交媒体查看个人主页:
GET /profile/zhangsan—— 路径中的用户名直接决定了展示谁的主页。 -
CMS内容管理删除文章:
DELETE /articles/9527—— 后台管理系统根据文章ID精确删除。 -
地理区域查询:
GET /weather/shanghai—— 获取特定城市的天气。
潜规则:路径参数必须必填且唯一,如果缺少它,路由根本无法匹配(直接返回 404)。
4. 参数校验(Path)
使用 Path 可以为路径参数添加业务校验(比如 ID 必须为正数):
python
from fastapi import Path
@app.get("/book/{id}")
def get_book(id: int = Path(..., ge=1, le=100, description="书籍ID,取值1-100")):
return {"id": id, "title": f"第{id}本书"}
二、查询参数(Query Parameters):细化“如何筛选与排序”
1. 什么是查询参数?
查询参数出现在 URL 的 ? 之后,以 key=value 的形式书写,多个用 & 分隔。例如 /search?keyword=python&page=2。
2. 定义方式
函数中未在路径 {} 中声明、且类型为基本类型的参数,会自动被识别为查询参数:
python
@app.get("/search")
def search(keyword: str, page: int = 1, limit: int = 10):
return {"关键词": keyword, "页码": page, "每页条数": limit}
3. 🔥 真实使用场景
查询参数专用于过滤、分页、排序和可选的附加条件。它不改变资源主体,只影响返回的结果集。
-
商品列表多条件筛选:
GET /products?category=手机&brand=华为&price_min=3000&stock=true—— 用户在前端勾选各种筛选项时,这些条件全部转为查询参数。 -
后台日志翻页与排序:
GET /logs?page=5&size=50&sort=-created_at—— 管理后台查看海量日志,必须靠查询参数做分页,-号代表降序。 -
全文搜索:
GET /videos?q=FastAPI教程&duration=short—— 搜索框输入的关键词天然适合放查询参数,因为可以加上时长、清晰度等辅助过滤。 -
开关与标识:
GET /report?export=true&format=pdf—— 控制是预览还是直接下载附件。
注意:查询参数支持可选(有默认值)或必填(无默认值)。由于数据明文暴露在 URL 中,绝对不要用来传递密码、Token 或身份证号。
4. 参数校验(Query)
使用 Query 可以轻松限制搜索词长度或价格范围:
python
from fastapi import Query
@app.get("/products")
def get_products(
name: str = Query(..., min_length=2, max_length=50, description="商品名称"),
price_min: float = Query(0, ge=0, description="最低价格")
):
return {"name": name, "price_min": price_min}
三、请求体参数(Request Body):承载“完整的新增或更新数据”
1. 什么是请求体?
请求体是放在 HTTP 请求的消息体(Body)中的数据,通常以 JSON 格式传输。它不在 URL 中,而是隐藏在请求的“信封”里。
2. 定义方式
请求体通过 Pydantic 模型来声明:
python
from pydantic import BaseModel
class User(BaseModel):
username: str
password: str
email: str | None = None # 可选字段
@app.post("/register")
def register(user: User):
return {"账号": user.username, "邮箱": user.email}
3. 🔥 真实使用场景
请求体专用于提交复杂、多层次、或涉及隐私的数据,主要集中在 POST/PUT/PATCH 请求中。它可以包含对象嵌套对象、数组等任意结构。
-
用户注册 / 登录:
POST /register包含 username、password、phone、captcha。密码是敏感信息,绝对不能进 URL,必须走请求体。 -
发布一篇带标签的博客:
POST /articles提交{"title":"...", "content":"...", "tags":["FastAPI","Python"], "category":{"id":5, "name":"后端"}}—— 这种嵌套结构只有请求体能优雅承载。 -
批量操作(如购物车结算):
POST /cart/checkout提交{"item_ids":[101,202,303], "coupon_code":"SAVE20"}—— 传递列表数据。 -
修改用户个人资料:
PUT /user/profile提交{"nickname":"新昵称", "avatar_url":"..."}—— 只更新特定字段。
黄金法则:GET 请求严禁带 Body(部分代理和服务器会直接丢弃或报错),POST/PUT/PATCH 必须用 Body。
4. 字段校验(Field)
使用 Field 可以约束请求体内每个字段的格式:
python
from pydantic import BaseModel, Field
class Item(BaseModel):
name: str = Field(..., min_length=1, max_length=100)
price: float = Field(..., gt=0, description="价格必须大于0")
stock: int = Field(default=0, ge=0)
四、三种参数对比与场景速查表
| 对比维度 | 路径参数 | 查询参数 | 请求体参数 |
|---|---|---|---|
| 位置 | URL 路径的一部分 | URL ? 之后的查询字符串 | HTTP 请求的消息体 |
| 业务语义 | “找谁”(资源定位) | “怎么找”(过滤分页) | “给什么”(数据提交) |
| 是否必填 | 必填 | 可选(可设默认值)或必填 | 取决于业务逻辑 |
| 常用方法 | GET / DELETE / PUT | 主要是 GET | POST / PUT / PATCH |
| 数据复杂程度 | 简单类型(int、str) | 简单类型(int、str、bool) | 复杂嵌套、数组、对象 |
| 安全敏感性 | 中 | 低(明文暴露在 URL,留痕浏览器历史) | 高(不出现在 URL 和访问日志中) |
| 典型业务场景 | 查看订单详情、删除用户、获取某商品 | 商品列表筛选、分页翻页、关键词搜索 | 注册登录、发布文章、修改配置、批量下单 |
五、混合使用:现实业务中的“组合拳”
实际开发中,这三者极少独立存在,往往是一起上阵的。FastAPI 最强大的地方就是能自动识别并各归其位。
考虑一个“修改某篇文章的评论设置”的真实接口:
python
from fastapi import FastAPI, Path, Query
from pydantic import BaseModel
app = FastAPI()
class CommentConfig(BaseModel):
allow_comment: bool # 是否允许评论
comment_audit: bool # 是否开启审核
auto_reply_text: str | None = None # 自动回复文案
@app.put("/articles/{article_id}/settings")
def update_comment_settings(
article_id: int = Path(..., ge=1, description="要操作的文章ID"), # 1. 路径参数:找资源
token: str = Query(..., description="操作人的鉴权Token"), # 2. 查询参数:携带鉴权标识(虽然不如Header安全,但实战中有人这样用)
config: CommentConfig = ... # 3. 请求体:具体的修改配置
):
return {
"操作文章": article_id,
"鉴权Token": token,
"新配置": config
}
在这个例子中:
-
路径参数告诉后端“动的是哪一篇文章”;
-
查询参数携带了本次请求的“上下文条件”(如临时标识、时间戳等);
-
请求体承载了这次“修改操作的所有详细配置”。
六、资深开发者的选型心法
在实际业务中,如何一眼看穿该用哪种参数?记住下面三句口诀:
-
只要是“数字ID/唯一编码/名称”来定位某个资源,毫不犹豫用路径参数。比如
/employees/{emp_id},这最符合 RESTful 直觉,且 URL 看起来干净整洁。 -
只要涉及到“翻页、排序、关键词模糊搜索、多条件筛选”,一律用查询参数。这能让你的 GET 接口保持“幂等性”(无论调多少次,只要参数不变,结果不变),并且方便前端在地址栏直接修改参数进行调试。
-
只要涉及到“JSON 对象、嵌套数组、密码、长文本”,必须用请求体。不仅是为了安全(防日志泄露),更是因为 URL 的长度是有限制的(不同浏览器/服务器限制不同),而请求体的大小限制宽松得多。
掌握这三种路由参数及其背后的业务场景,你就彻底吃透了 FastAPI 数据接收的精髓。配合 FastAPI 启动后自动生成的 /docs 交互式文档,前后端联调将变得无比丝滑。快去你的项目中实践一下吧!

868

被折叠的 条评论
为什么被折叠?



