路徑參數¶
你可以用與 Python 格式化字串相同的語法來宣告路徑「參數」或「變數」:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id):
return {"item_id": item_id}
路徑參數 item_id 的值會作為引數 item_id 傳入你的函式。
所以,如果你執行這個範例並前往 http://127.0.0.1:8000/items/foo,你會看到這樣的回應:
{"item_id":"foo"}
具型別的路徑參數¶
你可以在函式中使用標準的 Python 型別註記為路徑參數宣告型別:
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。
Check
這會在你的函式中提供編輯器支援,包括錯誤檢查、自動完成等。
資料 轉換¶
如果你執行這個範例並在瀏覽器開啟 http://127.0.0.1:8000/items/3,你會看到這樣的回應:
{"item_id":3}
Check
注意你的函式接收(並回傳)的值是 3,也就是 Python 的 int,而不是字串 "3"。
因此,有了這個型別宣告,FastAPI 會自動為你處理請求的 「解析」。
資料驗證¶
但如果你在瀏覽器前往 http://127.0.0.1:8000/items/foo,你會看到漂亮的 HTTP 錯誤:
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
因為路徑參數 item_id 的值是 "foo",它不是 int。
同樣的錯誤也會在你提供 float 而不是 int 時出現,例如:http://127.0.0.1:8000/items/4.2
Check
因此,搭配相同的 Python 型別宣告,FastAPI 會為你進行資料驗證。
注意錯誤也清楚指出驗證未通過的確切位置。
這在開發與除錯與你的 API 互動的程式碼時非常有幫助。
文件¶
當你在瀏覽器開啟 http://127.0.0.1:8000/docs,你會看到自動產生、可互動的 API 文件,例如:

Check
同樣地,只要使用那個 Python 型別宣告,FastAPI 就會提供自動、互動式的文件(整合 Swagger UI)。
注意路徑參數被宣告為整數。
基於標準的優勢與替代文件¶
而且因為產生的 schema 來自 OpenAPI 標準,有很多相容的工具可用。
因此,FastAPI 本身也提供另一種 API 文件(使用 ReDoc),你可以在 http://127.0.0.1:8000/redoc 存取:

同樣地,也有許多相容工具可用,包括支援多種語言的程式碼產生工具。
Pydantic¶
所有資料驗證都由 Pydantic 在底層處理,因此你能直接受惠。而且你可以放心使用。
你可以用相同的型別宣告搭配 str、float、bool 與許多更複雜的資料型別。
這些之中的好幾個會在接下來的教學章節中介紹。
順序很重要¶
在建立「路徑操作」時,你可能會遇到有固定路徑的情況。
像是 /users/me,假設它用來取得目前使用者的資料。
然後你也可能有一個路徑 /users/{user_id} 用來依使用者 ID 取得特定使用者的資料。
因為「路徑操作」會依宣告順序來比對,你必須確保 /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,並「認為」它收到一個值為 "me" 的 user_id 參數。
同樣地,你不能重新定義同一路徑操作:
from fastapi import FastAPI
app = FastAPI()
@app.get("/users")
async def read_users():
return ["Rick", "Morty"]
@app.get("/users")
async def read_users2():
return ["Bean", "Elfo"]
因為第一個會被優先使用(路徑先匹配到)。
預先定義的值¶
如果你有一個接收「路徑參數」的「路徑操作」,但你希望可用的「路徑參數」值是預先定義好的,你可以使用標準的 Python Enum。
建立 Enum 類別¶
匯入 Enum 並建立一個同時繼承自 str 與 Enum 的子類別。
繼承自 str 之後,API 文件就能知道這些值的型別必須是 string,並能正確地呈現。
然後建立具有固定值的類別屬性,這些就是可用的有效值:
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 is 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"}
Tip
如果你在想,「AlexNet」、「ResNet」和「LeNet」只是一些機器學習 模型 的名字。
宣告一個「路徑參數」¶
接著使用你建立的列舉類別(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 is 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"}
查看文件¶
因為「路徑參數」的可用值是預先定義的,互動式文件可以很漂亮地顯示它們:

使用 Python「列舉」¶
「路徑參數」的值會是一個「列舉成員」。
比較「列舉成員」¶
你可以把它與你建立的列舉 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 is 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 取得實際的值(在這個例子中是 str),一般而言就是 your_enum_member.value:
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 is 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"}
Tip
你也可以用 ModelName.lenet.value 取得值 "lenet"。
回傳「列舉成員」¶
你可以從「路徑操作」回傳「列舉成員」,即使是巢狀在 JSON 主體(例如 dict)裡。
在回傳給用戶端之前,它們會被轉成對應的值(此例為字串):
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 is 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"}
你的用戶端會收到像這樣的 JSON 回應:
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
包含路徑的路徑參數¶
假設你有一個路徑為 /files/{file_path} 的「路徑操作」。
但你需要 file_path 本身就包含一個「路徑」,像是 home/johndoe/myfile.txt。
所以,該檔案的 URL 會是:/files/home/johndoe/myfile.txt。
OpenAPI 支援¶
OpenAPI 並不支援直接宣告一個「路徑參數」內再包含一個「路徑」,因為那會導致難以測試與定義的情況。
然而,你仍可在 FastAPI 中這樣做,方法是使用 Starlette 的其中一個內部工具。
而文件依然能運作,只是它不會額外說明該參數應該包含一個路徑。
路徑轉換器¶
使用 Starlette 提供的選項,你可以用像這樣的 URL 來宣告一個包含「路徑」的「路徑參數」:
/files/{file_path:path}
在這個例子裡,參數名稱是 file_path,而最後面的 :path 表示該參數應該匹配任意「路徑」。
所以你可以這樣使用它:
from fastapi import FastAPI
app = FastAPI()
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"file_path": file_path}
Tip
你可能需要這個參數包含 /home/johndoe/myfile.txt,也就是前面帶有斜線(/)。
在那種情況下,URL 會是:/files//home/johndoe/myfile.txt,在 files 與 home 之間有兩個斜線(//)。
回顧¶
使用 FastAPI,只要撰寫簡短、直覺且標準的 Python 型別宣告,你就能獲得:
- 編輯器支援:錯誤檢查、自動完成等
- 資料 "解析"
- 資料驗證
- API 註解與自動產生文件
而且你只要宣告一次就好。
這大概是 FastAPI 相較於其他框架最明顯的優勢之一(除了原始效能之外)。