一、請求體和字段
1、基礎用法
請求體的數據校驗是使用Pydantic來進行聲明,然后校驗的。
from typing import Optional from fastapi import FastAPI from pydantic import BaseModel class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None app = FastAPI() @app.post("/items/") async def create_item(item: Item): print(item.name) # 獲取請求體中的值 return item
請求體內容通過Item類提前定義好,內容包含4個字段,其中description和tax為可選字段,所以請求體內容為:
{ "name": "hello", "price": 30.2 }
也是可行的。值得注意的是聲明參數時將其申明為定義好的Item類型。
2、配置項
如果調用方對於請求體不知道如何使用,可以通過額外的配置項進行說明:
... class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None class Config: schema_extra = { "example": { "name": "apple", "description": "this is a fruit ...", "price": 3.14, "tax": 1.2 } } ...
這樣在API接口處會有對應的示例說明:
3、字段
與使用Query、Path的方式相同,Field在Pydantic模型內部聲明校驗和元數據。
from typing import Optional from pydantic import BaseModel, Field class Item(BaseModel): name: str description: Optional[str] = Field(None, title="the description ...", max_length=20) price: float = Field(..., ge=2, description="price") tax: Optional[float] = None class Config: schema_extra = { "example": { "name": "apple", "description": "this is a fruit ...", "price": 3.14, "tax": 1.2 } } ...
注意,Field時從Pydantic中導入,這與Path與Query不同。
Field可以傳遞校驗的參數:
def Field( default: Any = Undefined, *, default_factory: Optional[NoArgAnyCallable] = None, alias: str = None, title: str = None, description: str = None, const: bool = None, gt: float = None, ge: float = None, lt: float = None, le: float = None, multiple_of: float = None, min_items: int = None, max_items: int = None, min_length: int = None, max_length: int = None, allow_mutation: bool = True, regex: str = None, **extra: Any, ) -> Any: ...
二、多參數混合
一個請求中可能有路徑參數、查詢參數以及請求體,當它們混合在一起又是如何處理的呢?不過在說明這個問題之前,需要注意的請求體中可能有多個請求體參數以及單個請求體參數。
(一)多個請求體參數
from typing import Optional from fastapi import FastAPI from pydantic import BaseModel, Field from datetime import date class Item(BaseModel): name: str description: Optional[str] = Field(None, title="the description ...", max_length=20) price: float = Field(..., ge=2, description="price") tax: Optional[float] = None class Config: schema_extra = { "example": { "name": "apple", "description": "this is a fruit ...", "price": 3.14, "tax": 1.2 } } class User(BaseModel): username: str full_name: Optional[str] = None birthday: date app = FastAPI() @app.post("/items/") async def create_item(item: Item, user: User): return {"item": item, "user": user}
上面聲明兩兩個請求體參數的模型,並且在返回過程中,鍵值分別時定義好的,所以被期望的返回值就是類似下面的:
{ "item": { "name": "apple", "description": "this is a fruit ...", "price": 3.14, "tax": 1.2 }, "user": { "username": "san", "full_name": "zhangsan", "birthday": "2021-06-03" } }
注意birthday字段的類型時date類型,也就是除了常用的數據類型:
- int
- float
- str
- bool
也可以定義其它復雜的數據類型:
- uuid(str表示)
- datetime.datetime (str表示,2021-06-03T15:53:00+05:00)
- datetime.date(str表示,2021-06-03)
- datetime.time(str表示,11:34:10.005)
- datetime.timedelta(float表示)
- bytes
- Decimal(float表示)
(二)單個請求體參數
在正常情況下,如果只有一個請求體模型,被期望的返回值應該是這樣的:
{ "name": "apple", "description": "this is a fruit ...", "price": 3.14, "tax": 1.2 }
但是如果希望達到和上面的多個請求體一樣的效果,外面存在一個鍵,內部是請求體的json內容,就需要使用Body中的embled參數了。
from typing import Optional from fastapi import FastAPI, Body from pydantic import BaseModel, Field class Item(BaseModel): name: str description: Optional[str] = Field(None, title="the description ...", max_length=20) price: float = Field(..., ge=2, description="price") tax: Optional[float] = None class Config: schema_extra = { "example": { "name": "apple", "description": "this is a fruit ...", "price": 3.14, "tax": 1.2 } } app = FastAPI() @app.post("/items/") async def create_item(item: Item = Body(..., embed=True)): return {"item": item}
其返回的結果就是:
{ "item": { "name": "apple", "description": "this is a fruit ...", "price": 3.14, "tax": 1.2 } }
(三)單值請求體參數
如果請求中是一個單值或者是請求體的一部分,單值不會像上述請求體一樣進行Pydantic模型設定,但是如果在函數的接受值中進行聲明,顯然FastAPI會將其當作一個查詢參數,這時需要使用Body,這與Query和Path類似。
from fastapi import FastAPI, Body app = FastAPI() @app.post("/single/value") async def single_value(importance: int = Body(...)): return {"importance": importance}
這樣,importance這個值會從請求體中傳遞給后台,而不是以查詢參數的輸入方式。
(四)多參數混合
from typing import Optional from fastapi import FastAPI, Body, Path from pydantic import BaseModel, Field from datetime import date class Item(BaseModel): name: str description: Optional[str] = Field(None, title="the description ...", max_length=20) price: float = Field(..., ge=2, description="price") tax: Optional[float] = None class Config: schema_extra = { "example": { "name": "apple", "description": "this is a fruit ...", "price": 3.14, "tax": 1.2 } } class User(BaseModel): username: str full_name: Optional[str] = None birthday: date app = FastAPI() @app.post("/multi/params/{item_id}") async def multi_params( *, item_id: int = Path(..., title="item id ...", ge=1, le=10), q: Optional[str] = None, item: Optional[Item] = None, user: User, importance: int = Body(...) ): results = {"item_id": item_id, "user": user, "importance": importance} if q: results.update({"q": q}) if item: results.update({"item": item}) return results
上述視圖函數中包含路徑參數、請求參數、多個請求體參數、單值請求體參數,接口中:
注意,第一個參數"*",Python不會對"*"做任何事情,但是之后所有的參數都應作為關鍵字(鍵值對),也被稱為kwargs來調用,即使它們沒有默認值。
三、嵌套模型
from typing import Optional, List, Set from fastapi import FastAPI from pydantic import BaseModel, HttpUrl app = FastAPI() class Image(BaseModel): url: HttpUrl # 對http進行驗證 name: str class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None tags: Set[str] = set() images: Optional[List[Image]] = None @app.put("/items/{item_id}") async def update_item(item_id: int, item: Item): return {"item_id": item_id, "item": item}
上述的請求體被期待這樣的請求格式:
{ "name": "item name", "description": "this is item...", "price": 0, "tax": 0, "tags": [], "images": [ { "url": "http://127.0.0.1:8000/docs", "name": "zhangsan" }, { "url": "http://127.0.0.1:8000/docs", "name": "lisi" } ] }
上述模型中:
- Image作為Item的一部分
- 聲明一些復雜的類型驗證(List、Set、HttpUrl等)
- 如果使用普通的Python list類型,無法對盛入的元素進行驗證