我們的web應用使用python web的fastapi框架,通過uvicorn開啟web服務。
1. refs
官網文檔:FastAPI (tiangolo.com)
github:https://github.com/tiangolo/fastapi
2. 環境配置
python:3.11+
uvicorn:0.29.0
pip install "uvicorn[standard]"
什么是uvicorn?
Uvicorn 是一個輕量級的 ASGI(Asynchronous Server Gateway Interface)服務器,用于運行 Python 的 ASGI 應用。ASGI 是一個標準接口,用于異步Web應用程序和服務器之間的通信,它允許你編寫異步代碼,從而提高應用程序的性能和可伸縮性。
Uvicorn 的主要特點包括:
- 異步支持:Uvicorn 完全支持異步,這意味著它可以處理大量的并發連接,而不會阻塞服務器。
- 性能:Uvicorn 提供了高性能的服務器能力,特別是在與異步框架(如 FastAPI 或 Starlette)結合使用時。
- 簡單易用:Uvicorn 的使用非常簡單,可以通過命令行啟動,也可以作為庫在代碼中啟動。
- 跨平臺:Uvicorn 可以在多種操作系統上運行,包括 Windows、macOS 和 Linux。
- 可擴展性:Uvicorn 可以輕松擴展以適應不同的工作負載,適用于從小規模到大規模的生產環境。
- 內置支持:許多現代 Python Web 框架,如 FastAPI 和 Starlette,已經內置了對 Uvicorn 的支持。
- 命令行接口:Uvicorn 提供了一個命令行接口(CLI),允許你快速啟動和管理 ASGI 應用。
- WebSockets 支持:Uvicorn 支持 WebSockets,使得實時通信和交互式應用的構建成為可能。
- Gunicorn 集成:Uvicorn 可以與 Gunicorn(一個 Python WSGI HTTP 服務器)集成,通過 Gunicorn 運行 Uvicorn 工作器。
FastAPI的官網介紹自己說是性能最好的Python Web框架之一,主要原因就是web端的Uvicorn和Starlette的功勞。
fastapi:0.111.0
pip install fastapi
可以看到fastapi是依賴于uvicorn做服務器的,所以務必下載這個依賴。
3. Start Off
3.1. 一個最簡單的例子
啟動服務
先來一個最簡單的示例:
# file:main.py
from fastapi import FastAPIapp = FastAPI()@app.get("/")
async def root():return {"message": "Hello World"}@app.get("/hello/{name}")
async def say_hello(name: str):return {"message": f"Hello {name}"}
隨后利用命令:
uvicorn main:app --reload
進行啟動
uvicorn main:app
?命令含義如下:
main
:main.py
?文件(一個 Python「模塊」)。app
:在?main.py
?文件中通過?app = FastAPI()
?創建的對象。-reload
:讓服務器在更新代碼后重新啟動。僅在開發時使用該選項。
隨后可以請求下我們創建的兩個接口:
可以看到服務端的所有請求:
同時,可以創建一個.http文件,進行接口測試:
接口文檔
跳轉到?http://127.0.0.1:8000/docs。
你將會看到自動生成的交互式 API 文檔(由?Swagger UI?提供)
以及由?ReDoc提供的可選的文檔
OpenAPI規范
FastAPI?使用定義 API 的?OpenAPI?標準將你的所有 API 轉換成一種模式描述,或者說是API的規范。
訪問127.0.0.1:8000/openapi.json可以看到這個json文檔。
3.2. 開放一個新的接口
路徑類型
這個在上面的實例中已經有所體現:
@app.get("/")
async def root():return {"message": "Hello World"}
這里的請求路徑方式有很多,包括:
@app.post()
@app.put()
@app.delete()
@app.options()
@app.head()
@app.patch()
@app.trace()
雖然在語義上有所不同,但前三個(post,put,delete)的實際行為是可以任意規定的。比如本來該delete的行為,用post來傳遞參數,其實也無所謂。
另外我們可以用async關鍵字來規定某個接口的行為是否是異步的,對于那些不需要等待其他子程序的請求,我們可以允許這樣的異步行為,利用await關鍵字告知python在這段程序執行時你可以轉而執行其他子程序,等到這段程序執行完畢再返回執行。
路徑參數
參數聲明+參數類型
@app.get("/items/{item_id}")
async def read_item(item_id: int):return {"item_id": item_id}
本例把?item_id
?的類型聲明為?int
。
同時會對這個參數進行校驗,如果你傳個沒法轉成int類型的數據,(比如food,4.2這種)將報錯:
{"detail": [{"loc": ["path","item_id"],"msg": "value is not a valid integer","type": "type_error.integer"}]
}
此外,這個類型可以是枚舉類型:路徑操作使用 Python 的?Enum
?類型接收預設的路徑參數。
導入?Enum
?并創建繼承自?str
?和?Enum
?的子類。
通過從?str
?繼承,API 文檔就能把值的類型定義為字符串,并且能正確渲染。
然后,創建包含固定值的類屬性,這些固定值是可用的有效值:
from enum import Enumfrom fastapi import FastAPIclass 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"}
最后,這個參數本身可能也是個路徑,例如/home/myfile.txt這種,此時要用到路徑轉換器:
from fastapi import FastAPIapp = FastAPI()@app.get("/files/{file_path:path}")
async def read_file(file_path: str):return {"file_path": file_path}
本例中,參數名為?file_path
,結尾部分的?:path
?說明該參數應匹配路徑。
查詢參數
在?后,用&分割
例如:
from fastapi import FastAPIapp = FastAPI()fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):return fake_items_db[skip : skip + limit]
利用該接口請求:
<http://127.0.0.1:8000/items/?skip=0&limit=10>
如果不指定這兩個參數,那么將是默認值
請求體
這里有一個很關鍵的組件,叫做pydantic,是python生態圈里很有名的做數據校驗的組件:Welcome to Pydantic - Pydantic
首先我們需要利用pydantic提供的basemodel來定義一個合法的請求體是什么樣子的:
from fastapi import FastAPI
from pydantic import BaseModelclass Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Noneapp = FastAPI()@app.post("/items/")
async def create_item(item: Item):return item
其中description和tax是有默認值的,可以不給他們傳參,但是沒有默認值得必須傳參了。
from fastapi import FastAPI
from pydantic import BaseModelclass Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Noneapp = FastAPI()@app.post("/items/")
async def create_item(item: Item):return item
此處,請求體參數的類型為?Item
?模型。 甚至請求體和路徑參數是可以共存的:
from fastapi import FastAPI
from pydantic import BaseModelclass Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Noneapp = FastAPI()@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):return {"item_id": item_id, **item.dict()}