跳至內容

路徑參數和數值驗證

就像您可以使用 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 時會發生錯誤。

在使用 Annotated 之前,請確保您已將 FastAPI 版本升級到至少 0.95.1。

宣告中繼資料

您可以宣告與 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 來說,這無關緊要。它會根據參數的名稱、類型和預設宣告(QueryPath 等)來檢測參數,它不關心順序。

因此,您可以將您的函式宣告為

提示

如果可能,建議使用 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,就不會有這個問題,因為您沒有使用 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,這可能不太重要或必要。

這裡有一個很方便的小技巧,但您不會經常需要它。

如果您想

  • 在不使用 Query 或任何預設值的情況下宣告 q 查詢參數
  • 使用 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

使用 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

數字驗證:大於或等於

使用 QueryPath(以及您稍後會看到的其他方法),您可以宣告數字限制。

在這裡,使用 ge=1item_id 將需要是一個「greater than or equal」於 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

數字驗證:大於和小於或等於

同樣適用於

  • gtgreater than(大於)
  • leless than or equal(小於或等於)
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.00 將無效。

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

摘要

使用 QueryPath(以及您尚未看到的其他方法),您可以以與查詢參數和字串驗證相同的方式宣告中繼資料和字串驗證。

您也可以宣告數字驗證

  • gtgreater than(大於)
  • gegreater than or equal(大於或等於)
  • ltless than(小於)
  • leless than or equal(小於或等於)

資訊

QueryPath 以及您稍後將看到的其他類別都是通用 Param 類別的子類別。

它們都共享您看到的用於額外驗證和中繼資料的相同參數。

「技術細節」

當您從 fastapi 導入 QueryPath 等時,它們實際上是函數。

呼叫這些函數時,會返回同名類別的實例。

因此,您導入 Query,它是一個函數。當您呼叫它時,它會返回一個也名為 Query 的類別的實例。

這些函數的存在(而不是直接使用類別)是為了讓您的編輯器不會標記有關其類型的錯誤。

這樣,您可以使用普通的編輯器和編碼工具,而無需添加自定義配置來忽略這些錯誤。