|
| 1 | ++++ |
| 2 | +date = '2025-08-06T10:30:00+08:00' |
| 3 | +draft = false |
| 4 | +title = 'FastAPI Parameters' |
| 5 | ++++ |
| 6 | +FastAPI 是一个现代、快速(高性能)的 Python Web 框架, 它自动处理参数的解析、验证和文档生成 |
| 7 | + |
| 8 | +本文将介绍 FastAPI 中三类最常用的参数: **路径参数(Path Parameters)**、**查询参数(Query Parameters)**和**请求体(Request Body)**的用法与原理 |
| 9 | + |
| 10 | + |
| 11 | + |
| 12 | + |
| 13 | +### 1. Path Parameters 路径参数 |
| 14 | +路径参数是 URL 路径中的动态部分, 使用 `{}` 包裹表示 |
| 15 | +```python |
| 16 | +from fastapi import FastAPI |
| 17 | + |
| 18 | +app = FastAPI() |
| 19 | + |
| 20 | +@app.get("/items/{item_id}") |
| 21 | +async def read_item(item_id: str): |
| 22 | + return {"item_id": item_id} |
| 23 | +``` |
| 24 | +访问 `/items/foo` 返回: |
| 25 | +```python |
| 26 | +{"item_id": "foo"} |
| 27 | +``` |
| 28 | + |
| 29 | +#### Data conversion & validation 类型声明与自动转换 |
| 30 | +可以为路径参数声明类型, FastAPI 会自动解析并验证: |
| 31 | +```python |
| 32 | +@app.get("/items/{item_id}") |
| 33 | +async def read_item(item_id: int): |
| 34 | + return {"item_id": item_id} |
| 35 | +``` |
| 36 | +访问 `/items/3`, `item_id` 会被转换为 int 类型 |
| 37 | + |
| 38 | +#### Routing orders 路由匹配顺序 |
| 39 | +路径匹配按声明顺序执行, 例如 |
| 40 | +```python |
| 41 | +@app.get("/users/me") |
| 42 | +async def read_user_me(): |
| 43 | + return {"user_id": "current_user"} |
| 44 | + |
| 45 | +@app.get("/users/{user_id}") |
| 46 | +async def read_user(user_id: str): |
| 47 | + return {"user_id": user_id} |
| 48 | +``` |
| 49 | +必须先声明 `/users/me`, 否则会被 `/users/{user_id}` 捕获 |
| 50 | + |
| 51 | + |
| 52 | +#### Predefined enum values |
| 53 | +使用 Python 的 `Enum` 定义一组可选的路径参数值 |
| 54 | +```python |
| 55 | +from enum import Enum |
| 56 | + |
| 57 | +class ModelName(str, Enum): |
| 58 | + alexnet = "alexnet" |
| 59 | + resnet = "resnet" |
| 60 | + lenet = "lenet" |
| 61 | + |
| 62 | +@app.get("/models/{model_name}") |
| 63 | +async def get_model(model_name: ModelName): |
| 64 | + return {"model_name": model_name} |
| 65 | +``` |
| 66 | +Swagger 文档会自动显示可选值 |
| 67 | + |
| 68 | + |
| 69 | +#### Path parameters containing paths 路径型参数 |
| 70 | +默认路径参数不能包含斜杠 `/`, 但可以用 `:path` 声明允许匹配完整路径 |
| 71 | +```python |
| 72 | +@app.get("/files/{file_path:path}") |
| 73 | +async def read_file(file_path: str): |
| 74 | + return {"file_path": file_path} |
| 75 | +``` |
| 76 | +访问 `/files/home/user/file.txt`, `file_path` 会是 `"home/user/file.txt"` |
| 77 | + |
| 78 | + |
| 79 | + |
| 80 | + |
| 81 | +### 2. Query Parameters 查询参数 |
| 82 | +查询参数是 URL `?` 后的键值对, 不属于路径部分 |
| 83 | +```python |
| 84 | +fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}] |
| 85 | + |
| 86 | +@app.get("/items/") |
| 87 | +async def read_items(skip: int = 0, limit: int = 10): |
| 88 | + return fake_items_db[skip : skip + limit] |
| 89 | +``` |
| 90 | +访问 `/items/?skip=0&limit=10` 时, 会自动把查询参数 `skip` 和 `limit` 转成 `int` |
| 91 | + |
| 92 | +#### Optional parameters 可选参数默认值 |
| 93 | +给查询参数赋默认值即为可选 |
| 94 | +```python |
| 95 | +@app.get("/items/{item_id}") |
| 96 | +async def read_item(item_id: str, q: str | None = None): |
| 97 | + if q: |
| 98 | + return {"item_id": item_id, "q": q} |
| 99 | + return {"item_id": item_id} |
| 100 | +``` |
| 101 | +`q` 是可选查询参数, 默认为 `None` |
| 102 | + |
| 103 | +#### Query parameter type conversion 查询参数类型转换 |
| 104 | +```python |
| 105 | +@app.get("/items/{item_id}") |
| 106 | +async def read_item(item_id: str, q: str | None = None, short: bool = False): |
| 107 | + ... |
| 108 | +``` |
| 109 | +支持自动把字符串转换成布尔值, 以下都会被识别为 `True` |
| 110 | +``` |
| 111 | +http://127.0.0.1:8000/items/foo?short=1 |
| 112 | +``` |
| 113 | +或者 |
| 114 | +- `?short=true` |
| 115 | +- `?short=on` |
| 116 | +- `?short=yes` |
| 117 | + |
| 118 | +#### Multiple path and query parameters 多路径查询参数组合 |
| 119 | +路径参数和查询参数可混合使用, 无需声明顺 |
| 120 | +```python |
| 121 | +@app.get("/users/{user_id}/items/{item_id}") |
| 122 | +async def read_user_item(user_id: int, item_id: str, q: str | None = None, short: bool = False): |
| 123 | + ... |
| 124 | +``` |
| 125 | + |
| 126 | +#### Required query parameters 必填查询参数 |
| 127 | +未设置默认值的查询参数为必填参数 |
| 128 | +```python |
| 129 | +@app.get("/items/{item_id}") |
| 130 | +async def read_item(item_id: str, needy: str): |
| 131 | + ... |
| 132 | +``` |
| 133 | +上面的 `needy` 就是一个必填的 `str` 类型 |
| 134 | + |
| 135 | +当然也可以定义一些必填参数, 以及有默认值的可选参数 |
| 136 | +```python |
| 137 | +@app.get("/items/{item_id}") |
| 138 | +async def read_user_item( |
| 139 | + item_id: str, needy: str, skip: int = 0, limit: int | None = None |
| 140 | +): |
| 141 | + ... |
| 142 | +``` |
| 143 | +- `needy` & `item_id`, 必填 `str` 类型 |
| 144 | +- `skip`, 默认值为 0 的类型 |
| 145 | +- `limit`, 一个可选的类型 |
| 146 | + |
| 147 | +[注] |
| 148 | +- 路径参数永远是必填的, 因为它们来自 URL 本身 |
| 149 | + ```python |
| 150 | + @app.get("/items/{item_id}") |
| 151 | + def read(item_id: str = "123"): # 这里写默认值是无效的 |
| 152 | + ... |
| 153 | + ``` |
| 154 | +- 类型为 `Optional[...]` 或 `type | None` 不等于可选参数, 仍然要配合默认值 `= None` 才是可选 |
| 155 | + ```python |
| 156 | + def func(x: int | None): # 必填 |
| 157 | + def func(x: int | None = None): # 可选 |
| 158 | + ``` |
| 159 | + |
| 160 | + |
| 161 | + |
| 162 | + |
| 163 | +### 3. Request Body |
| 164 | +当通过 API 传送数据的时候, 通常通过 request body 发送 |
| 165 | + |
| 166 | +request body 是 client 客户端发送给 API 的数据, 而 response body 是 API 发送给 client 的数据 |
| 167 | + |
| 168 | +#### Pydantic's BaseModel |
| 169 | +使用 Pydantic 定义数据模型 |
| 170 | +```python |
| 171 | +from fastapi import FastAPI |
| 172 | +from pydantic import BaseModel |
| 173 | + |
| 174 | +class Item(BaseModel): |
| 175 | + name: str |
| 176 | + description: str | None = None |
| 177 | + price: float |
| 178 | + tax: float | None = None |
| 179 | + |
| 180 | +app = FastAPI() |
| 181 | + |
| 182 | +@app.post("/items/") |
| 183 | +async def create_item(item: Item): |
| 184 | + return item |
| 185 | +``` |
| 186 | + |
| 187 | +#### Declare it as a parameter |
| 188 | +在路由中声明请求体 |
| 189 | +```python |
| 190 | +@app.post("/items/") |
| 191 | +async def create_item(item: Item): |
| 192 | + return item |
| 193 | +``` |
| 194 | +FastAPI 会: |
| 195 | +- 读取 request body, 并转换为 JSON |
| 196 | +- 校验字段和类型 |
| 197 | + - 返回类型错误时给出详细反馈, 包括数据那里以及导致了什么错误 |
| 198 | +- 提供编辑器类型提示 |
| 199 | +- 生成模型的 JSON Schema 定义, 也可以在项目中任何位置使用 |
| 200 | +- 根据 schema 自动生成文档 |
| 201 | + |
| 202 | +#### Request body + path + query parameters 路径参数、查询参数与请求体同时使用 |
| 203 | +```python |
| 204 | +@app.put("/items/{item_id}") |
| 205 | +async def update_item(item_id: int, item: Item, q: str | None = None): |
| 206 | + result = {"item_id": item_id, **item.dict()} |
| 207 | + if q: |
| 208 | + result.update({"q": q}) |
| 209 | + return result |
| 210 | +``` |
| 211 | +这个函数参数会被以下方式识别: |
| 212 | +- 如果参数同时在 path 中声明, 被当成 path parameter |
| 213 | +- 如果参数为单一类型, 如 `int`, `float`, `str` 或 `bool` 等, 将会被解释为 query parameter |
| 214 | +- 如果参数声明为一个 Pydantic Model, 将被解释为 request body |
0 commit comments