FastAPI教程 路徑參數(shù)

2022-08-20 11:30 更新

你可以使用與 Python 格式化字符串相同的語法來聲明路徑"參數(shù)"或"變量":

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/{item_id}")
async def read_item(item_id):
    return {"item_id": item_id}

路徑參數(shù) ?item_id ?的值將作為參數(shù) ?item_id ?傳遞給你的函數(shù)。

所以,如果你運行示例并訪問 http://127.0.0.1:8000/items/foo,將會看到如下響應(yīng):

{"item_id":"foo"}

有類型的路徑參數(shù)

你可以使用標(biāo)準(zhǔn)的 Python 類型標(biāo)注為函數(shù)中的路徑參數(shù)聲明類型。

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

在這個例子中,?item_id ?被聲明為 ?int ?類型。

這將為你的函數(shù)提供編輯器支持,包括錯誤檢查、代碼補全等等。

數(shù)據(jù)轉(zhuǎn)換

如果你運行示例并打開瀏覽器訪問 http://127.0.0.1:8000/items/3,將得到如下響應(yīng):

{"item_id":3}
注意函數(shù)接收(并返回)的值為 3,是一個 Python ?int ?值,而不是字符串 ?"3"?。
所以,FastAPI 通過上面的類型聲明提供了對請求的自動"解析"。

數(shù)據(jù)校驗

但如果你通過瀏覽器訪問 http://127.0.0.1:8000/items/foo,你會看到一個清晰可讀的 HTTP 錯誤:

{
    "detail": [
        {
            "loc": [
                "path",
                "item_id"
            ],
            "msg": "value is not a valid integer",
            "type": "type_error.integer"
        }
    ]
}

因為路徑參數(shù) ?item_id ?傳入的值為 ?"foo"?,它不是一個? int?。

如果你提供的是 ?float ?而非整數(shù)也會出現(xiàn)同樣的錯誤,比如: http://127.0.0.1:8000/items/4.2

所以,通過同樣的 Python 類型聲明,???FastAPI??? 提供了數(shù)據(jù)校驗功能。
注意上面的錯誤同樣清楚地指出了校驗未通過的具體原因。
在開發(fā)和調(diào)試與你的 API 進行交互的代碼時,這非常有用。

文檔

當(dāng)你打開瀏覽器訪問 http://127.0.0.1:8000/docs,你將看到自動生成的交互式 API 文檔:

再一次,還是通過相同的 Python 類型聲明,FastAPI 為你提供了自動生成的交互式文檔(集成 Swagger UI)。

注意這里的路徑參數(shù)被聲明為一個整數(shù)。

基于標(biāo)準(zhǔn)的好處:可選文檔

由于生成的 API 模式來自于 OpenAPI 標(biāo)準(zhǔn),所以有很多工具與其兼容。

正因如此,FastAPI 內(nèi)置了一個可選的 API 文檔(使用 Redoc):

同樣的,還有很多其他兼容的工具,包括適用于多種語言的代碼生成工具。

Pydantic

所有的數(shù)據(jù)校驗都由 Pydantic 在幕后完成,所以你可以從它所有的優(yōu)點中受益。并且你知道它在這方面非常勝任。

你可以使用同樣的類型聲明來聲明 ?str?、?float?、?bool ?以及許多其他的復(fù)合數(shù)據(jù)類型。

本教程的下一章節(jié)將探討其中的一些內(nèi)容。

順序很重要

在創(chuàng)建路徑操作時,你會發(fā)現(xiàn)有些情況下路徑是固定的。

比如 ?/users/me?,我們假設(shè)它用來獲取關(guān)于當(dāng)前用戶的數(shù)據(jù).

然后,你還可以使用路徑? /users/{user_id} ?來通過用戶 ID 獲取關(guān)于特定用戶的數(shù)據(jù)。

由于路徑操作是按順序依次運行的,你需要確保路徑 ?/users/me? 聲明在路徑 ?/users/{user_id}?之前:

from fastapi import FastAPI

app = FastAPI()


@app.get("/users/me")
async def read_user_me():
    return {"user_id": "the current user"}


@app.get("/users/{user_id}")
async def read_user(user_id: str):
    return {"user_id": user_id}

否則,?/users/{user_id}? 的路徑還將與? /users/me? 相匹配,"認(rèn)為"自己正在接收一個值為 ?"me"? 的 ?user_id ?參數(shù)。

預(yù)設(shè)值

如果你有一個接收路徑參數(shù)的路徑操作,但你希望預(yù)先設(shè)定可能的有效參數(shù)值,則可以使用標(biāo)準(zhǔn)的 Python? Enum ?類型。

創(chuàng)建一個 Enum 類

導(dǎo)入 ?Enum ?并創(chuàng)建一個繼承自 ?str ?和 ?Enum? 的子類。

通過從 ?str ?繼承,API 文檔將能夠知道這些值必須為 ?string ?類型并且能夠正確地展示出來。

然后創(chuàng)建具有固定值的類屬性,這些固定值將是可用的有效值:

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name == ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}


枚舉(或 enums)從 3.4 版本起在 Python 中可用。

如果你想知道,"AlexNet"、"ResNet" 和 "LeNet" 只是機器學(xué)習(xí)中的模型名稱。

聲明路徑參數(shù)

然后使用你定義的枚舉類(ModelName)創(chuàng)建一個帶有類型標(biāo)注的路徑參數(shù):

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name == ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

查看文檔

因為已經(jīng)指定了路徑參數(shù)的可用值,所以交互式文檔可以恰當(dāng)?shù)卣故舅鼈儯?/p>

使用 Python 枚舉類型

路徑參數(shù)的值將是一個枚舉成員。

比較枚舉成員

你可以將它與你創(chuàng)建的枚舉類 ?ModelName ?中的枚舉成員進行比較:

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name == ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

獲取枚舉值

你可以使用 ?model_name.value ?或通常來說 ?your_enum_member.value ?來獲取實際的值(在這個例子中為 str):

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name == ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

你也可以通過 ?ModelName.lenet.value? 來獲取值 ?"lenet"?。

返回枚舉成員

你可以從路徑操作中返回枚舉成員,即使嵌套在 JSON 結(jié)構(gòu)中(例如一個 dict 中)。

在返回給客戶端之前,它們將被轉(zhuǎn)換為對應(yīng)的值:

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name == ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

包含路徑的路徑參數(shù)

假設(shè)你有一個路徑操作,它的路徑為? /files/{file_path}?。

但是你需要 ?file_path ?自身也包含路徑,比如 ?home/johndoe/myfile.txt?。

因此,該文件的URL將類似于這樣:?/files/home/johndoe/myfile.txt?。

OpenAPI 支持

OpenAPI 不支持任何方式去聲明路徑參數(shù)以在其內(nèi)部包含路徑,因為這可能會導(dǎo)致難以測試和定義的情況出現(xiàn)。

不過,你仍然可以通過 Starlette 的一個內(nèi)部工具在 FastAPI 中實現(xiàn)它。

而且文檔依舊可以使用,但是不會添加任何該參數(shù)應(yīng)包含路徑的說明。

路徑轉(zhuǎn)換器

你可以使用直接來自 Starlette 的選項來聲明一個包含路徑的路徑參數(shù):

/files/{file_path:path}

在這種情況下,參數(shù)的名稱為 ?file_path?,結(jié)尾部分的 :?path ?說明該參數(shù)應(yīng)匹配任意的路徑。

因此,你可以這樣使用它:

from fastapi import FastAPI

app = FastAPI()


@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
    return {"file_path": file_path}
你可能會需要參數(shù)包含 ?/home/johndoe/myfile.txt?,以斜杠(/)開頭。
在這種情況下,URL 將會是 ?/files//home/johndoe/myfile.txt?,在files 和 home 之間有一個雙斜杠(//)。

總結(jié)

使用 FastAPI,通過簡短、直觀和標(biāo)準(zhǔn)的 Python 類型聲明,你將獲得:

  • 編輯器支持:錯誤檢查,代碼補全等
  • 數(shù)據(jù) "解析"
  • 數(shù)據(jù)校驗
  • API 標(biāo)注和自動生成的文檔

而且你只需要聲明一次即可。

這可能是 FastAPI 與其他框架相比主要的明顯優(yōu)勢(除了原始性能以外)。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號