路径参数和数值验证¶
与使用 Query
为查询参数声明更多验证和元数据的方式相同,你也可以使用 Path
为路径参数声明相同类型的验证和元数据。
导入 Path¶
首先,从 fastapi
导入 Path
,并导入 Annotated
from typing import Annotated
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")],
q: Annotated[str | None, Query(alias="item-query")] = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
from typing import Annotated, Union
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")],
q: Annotated[Union[str, None], Query(alias="item-query")] = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
from typing import Union
from fastapi import FastAPI, Path, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")],
q: Annotated[Union[str, None], Query(alias="item-query")] = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
提示
如果可能,请优先使用 Annotated
版本。
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: int = Path(title="The ID of the item to get"),
q: str | None = Query(default=None, alias="item-query"),
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
提示
如果可能,请优先使用 Annotated
版本。
from typing import Union
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: int = Path(title="The ID of the item to get"),
q: Union[str, None] = Query(default=None, alias="item-query"),
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
信息
FastAPI 在 0.95.0 版本中增加了对 Annotated
的支持(并开始推荐它)。
如果你的版本较旧,尝试使用 Annotated
时会报错。
请确保你将 FastAPI 版本升级到至少 0.95.1 后再使用 Annotated
。
声明元数据¶
你可以声明与 Query
相同的所有参数。
例如,要为路径参数 item_id
声明一个 title
元数据值,你可以这样写
from typing import Annotated
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")],
q: Annotated[str | None, Query(alias="item-query")] = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
from typing import Annotated, Union
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")],
q: Annotated[Union[str, None], Query(alias="item-query")] = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
from typing import Union
from fastapi import FastAPI, Path, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")],
q: Annotated[Union[str, None], Query(alias="item-query")] = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
提示
如果可能,请优先使用 Annotated
版本。
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: int = Path(title="The ID of the item to get"),
q: str | None = Query(default=None, alias="item-query"),
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
提示
如果可能,请优先使用 Annotated
版本。
from typing import Union
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: int = Path(title="The ID of the item to get"),
q: Union[str, None] = Query(default=None, alias="item-query"),
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
注意
路径参数总是必需的,因为它必须是路径的一部分。即使你将其声明为 None
或设置了默认值,它也不会产生任何影响,它仍然始终是必需的。
按需排序参数¶
提示
如果你使用 Annotated
,这可能不那么重要或不必要。
假设你想声明查询参数 q
为必需的 str
类型。
并且你不需要为该参数声明任何其他内容,所以你实际上不需要使用 Query
。
但是你仍然需要为 item_id
路径参数使用 Path
。并且由于某些原因你不想使用 Annotated
。
如果你将带有“默认值”的参数放在没有“默认值”的参数之前,Python 会报错。
但你可以重新排序它们,将没有默认值的参数(查询参数 q
)放在前面。
这对 FastAPI 来说无关紧要。它会根据参数的名称、类型和默认声明(Query
、Path
等)来检测参数,它不关心顺序。
所以,你可以将函数声明为
提示
如果可能,请优先使用 Annotated
版本。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(q: str, item_id: int = Path(title="The ID of the item to get")):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
q: str, item_id: Annotated[int, Path(title="The ID of the item to get")]
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
from fastapi import FastAPI, Path
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
q: str, item_id: Annotated[int, Path(title="The ID of the item to get")]
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
但请记住,如果你使用 Annotated
,就不会出现这个问题,因为你没有为 Query()
或 Path()
使用函数参数的默认值,所以顺序无关紧要。
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
q: str, item_id: Annotated[int, Path(title="The ID of the item to get")]
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
from fastapi import FastAPI, Path
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
q: str, item_id: Annotated[int, Path(title="The ID of the item to get")]
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
提示
如果可能,请优先使用 Annotated
版本。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(q: str, item_id: int = Path(title="The ID of the item to get")):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
按需排序参数,小技巧¶
提示
如果你使用 Annotated
,这可能不那么重要或不必要。
这里有一个可能很有用但你不会经常用到的小技巧。
如果你想
- 声明
q
查询参数,但不使用Query
或任何默认值 - 使用
Path
声明路径参数item_id
- 让它们以不同的顺序排列
- 不使用
Annotated
……Python 为此提供了一个特殊的小语法。
将 *
作为函数的第一个参数传入。
Python 不会对那个 *
做任何事情,但它会知道所有后续参数都应该作为关键字参数(键值对)调用,也被称为 kwargs
。即使它们没有默认值。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(*, item_id: int = Path(title="The ID of the item to get"), q: str):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")], q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
from fastapi import FastAPI, Path
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")], q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
使用 Annotated
更优¶
请记住,如果你使用 Annotated
,因为你没有使用函数参数的默认值,你就不会遇到这个问题,而且你可能不需要使用 *
。
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")], q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
from fastapi import FastAPI, Path
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")], q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
提示
如果可能,请优先使用 Annotated
版本。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(*, item_id: int = Path(title="The ID of the item to get"), q: str):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
数值验证:大于或等于¶
使用 Query
和 Path
(以及你稍后会看到的其他内容),你可以声明数值约束。
这里,使用 ge=1
,item_id
需要是一个“g
reater than or e
qual”(大于或等于)1
的整数。
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get", ge=1)], q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
from fastapi import FastAPI, Path
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get", ge=1)], q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
提示
如果可能,请优先使用 Annotated
版本。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*, item_id: int = Path(title="The ID of the item to get", ge=1), q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
数值验证:大于和小于或等于¶
同样适用于
gt
:g
reatert
han (大于)le
:l
ess than ore
qual (小于或等于)
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get", gt=0, le=1000)],
q: str,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
from fastapi import FastAPI, Path
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get", gt=0, le=1000)],
q: str,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
提示
如果可能,请优先使用 Annotated
版本。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*,
item_id: int = Path(title="The ID of the item to get", gt=0, le=1000),
q: str,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
数值验证:浮点数、大于和小于¶
数值验证也适用于 float
值。
这就是为什么能够声明 gt
而不仅仅是 ge
变得很重要的原因。有了它,你可以要求一个值必须大于 0
,即使它小于 1
。
因此,0.5
将是一个有效值。但 0.0
或 0
则不是。
对于 lt
也是如此。
from typing import Annotated
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*,
item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
q: str,
size: Annotated[float, Query(gt=0, lt=10.5)],
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if size:
results.update({"size": size})
return results
🤓 其他版本和变体
from fastapi import FastAPI, Path, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*,
item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
q: str,
size: Annotated[float, Query(gt=0, lt=10.5)],
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if size:
results.update({"size": size})
return results
提示
如果可能,请优先使用 Annotated
版本。
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*,
item_id: int = Path(title="The ID of the item to get", ge=0, le=1000),
q: str,
size: float = Query(gt=0, lt=10.5),
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if size:
results.update({"size": size})
return results
总结¶
使用 Query
、Path
(以及你尚未见过的其他内容),你可以像 查询参数和字符串验证 那样声明元数据和字符串验证。
你也可以声明数值验证:
gt
:g
reatert
han (大于)ge
:g
reater than ore
qual (大于或等于)lt
:l
esst
han (小于)le
:l
ess than ore
qual (小于或等于)
信息
Query
、Path
以及你稍后将看到的其他类都是一个共同的 Param
类的子类。
所有这些类都共享你已经看到过的用于额外验证和元数据的相同参数。
技术细节
当你从 fastapi
导入 Query
、Path
和其他内容时,它们实际上是函数。
当它们被调用时,会返回同名类的实例。
所以,你导入的是 Query
函数。当你调用它时,它会返回一个同样名为 Query
的类的实例。
提供这些函数(而不是直接使用类)是为了让你的编辑器不会标记关于其类型的错误。
这样,你就可以使用你常用的编辑器和编码工具,而无需添加自定义配置来忽略这些错误。