二、路径参数(路径变量)
URL路径里的变量,用{变量名},函数参数接收。FastAPI自动做类型校验。
# 路径参数
@app.get("/items/{item_id}")
def read_item(item_id: int): # 自动转int,非数字直接返回422校验错误
return {"item_id": item_id}
路径参数优先级规则
静态路径 > 变量路径,顺序从上到下匹配,先定义先匹配
@app.get("/items/me") # 静态路径,优先匹配
def read_me():
return {"me": True}
@app.get("/items/{item_id}")
def read_item(item_id:int):
return {"id":item_id}
⚠️ 如果把/{item_id}写在/me前面,访问/items/me会把me赋值给item_id,报int转换异常。
路径参数带枚举限制
from enum import Enum
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
@app.get("/models/{model_name}")
def get_model(model_name: ModelName):
return {"model": model_name}
路径包含路径(path类型,匹配/)
/files/{file_path:path}可以匹配带斜杠的路径,例如/files/a/b/c.txt
@app.get("/files/{file_path:path}")
def read_file(file_path: str):
return {"file_path": file_path}
三、查询参数(Query参数,url?key=value)
不在路径中,在?后面,函数普通参数就是查询参数。
@app.get("/items/")
def read_items(skip: int = 0, limit: int | None = None):
return {"skip": skip, "limit": limit}
- 带默认值:
skip:int=0,不传使用默认 limit: int | None = None:可选参数,可以不传
Query高级校验
from fastapi import Query
@app.get("/products")
def qry(
page: int = Query(1, ge=1, description="页码>=1"),
keyword: str | None = Query(None, min_length=2, max_length=20)
):
return locals()
四、路由元信息(装饰器参数)
@app.get(
"/items/{item_id}",
summary="获取单个商品",
description="根据id获取商品详情",
tags=["商品管理"], # openapi文档分组
response_description="返回商品json",
deprecated=False, # 是否标记接口废弃
status_code=200, # 默认响应状态码
)
def get_item(item_id:int):
return {"id":item_id}
五、APIRouter 模块化路由(大型项目必用)
把路由拆分到不同文件,实现接口模块化,统一加前缀、tags。
items.py
from fastapi import APIRouter
router = APIRouter(
prefix="/items", # 该组所有路由自动拼接前缀
tags=["商品模块"]
)
@router.get("/")
def list_items():
return {"data":"商品列表"}
@router.get("/{item_id}")
def get_one(item_id:int):
return {"id":item_id}
main.py
from fastapi import FastAPI
from items import router as item_router
app = FastAPI()
# 注册子路由
app.include_router(item_router)
APIRouter 参数说明
prefix:统一路径前缀,不要末尾写/tags:分组标签dependencies:该路由组全部接口共用依赖responses:统一预设错误响应
还可以嵌套路由:
include_router可以多次嵌套。
六、路由的依赖 dependencies
单接口依赖
from fastapi import Depends
def get_token():
return "abc123"
@app.get("/secret", dependencies=[Depends(get_token)])
def secret():
return {"ok":True}
APIRouter全局依赖,组内全部接口生效
router = APIRouter(
prefix="/admin",
dependencies=[Depends(check_admin)]
)
app全局依赖(所有接口)
app = FastAPI(dependencies=[Depends(global_auth)])
七、路由响应配置
response_model:过滤输出字段,Pydantic模型status_code:返回状态码responses:额外文档的错误码示例
```python
from pydantic import BaseModel
class ItemOut(BaseModel):
id:int
name:str
@app.get(“/item/{id}”, response_model=ItemOut, status_code=200, responses={404:{“description”:”找不到”}})
def get_item(id:int):
return {“id”:id,”name”:”xxx”,”secret”:”不会返回”}
## 八、路由匹配原理
1. 请求进来,starlette遍历注册的路由列表,从上往下匹配;
2. 优先完全静态匹配,再匹配路径参数;
3. 匹配成功执行对应函数;
4. 函数参数解析来源:路径参数、query、body、header、cookie、依赖;
5. pydantic校验参数,校验失败返回422;
6. 返回值自动json序列化。
> ⚠️重要:**路由定义顺序很关键,静态路由写在变量路由前面,否则会被变量路由拦截**。
## 九、特殊路由
### 1. 重定向 RedirectResponse
```python
from fastapi.responses import RedirectResponse
@app.get("/go")
def go():
return RedirectResponse("/docs")
2. 文件响应 FileResponse
from fastapi.responses import FileResponse
@app.get("/download")
def download():
return FileResponse("test.txt")
3. 挂载子应用(mount)
把另外一个starlette/fastapi应用挂载到某个路径下,不是路由,是完整子应用。
from fastapi import FastAPI
sub_app = FastAPI()
@sub_app.get("/hello")
def h():return {"hi":"sub"}
app = FastAPI()
app.mount("/sub", sub_app)
# 访问 /sub/hello
mount和include_router区别:
include_router:只是把路由规则合并到主app,共享文档;mount:挂载完整独立应用,openapi文档不会合并。
十、常用路由调试工具
访问自动文档:
- SwaggerUI:
/docs - ReDoc:
/redoc
完整项目目录示例(模块化)
main.py
routers/
__init__.py
user.py
goods.py
main.py
from fastapi import FastAPI
from routers.user import router as user_router
from routers.goods import router as goods_router
app = FastAPI(title="路由演示")
app.include_router(user_router)
app.include_router(goods_router)
routers/user.py
from fastapi import APIRouter
router = APIRouter(prefix="/user", tags=["用户"])
@router.get("/list")
def user_list():
return {"users":[]}
常见坑总结
- 路由顺序错误,静态接口被路径参数接口拦截;
prefix="/items/"末尾多斜杠,导致拼接双斜杠;推荐prefix="/items";- 路径参数类型写错,请求直接422;
mount和include_router混淆,子应用看不到文档;- POST接口,把参数写为普通函数参数,误当成query参数(body参数需要pydantic模型)。
如果你需要,我可以给你一份可直接运行完整示例代码,或者讲路由内部源码解析。