FastAPI請求系列(三) Request Body


一、請求體和字段

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類型,無法對盛入的元素進行驗證

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM