一、概述
一般對於Request Body不會通過get提交,對於get提交的參數一般稱為是查詢參數。所以,如果是通過POTS,PUT等方式提交的參數信息,我們一般是放到Request Body來提交到我們的后端。
對於如何接收和校驗請求體,FastApi提供的形式是使用:from pydantic import BaseModel
示例如下:
import uvicorn from fastapi import FastAPI from pydantic import BaseModel class Item(BaseModel): name: str description: str = None price: float tax: float = None app = FastAPI() @app.post("/items/") async def create_item(item: Item): return item if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
在上面的模型中,如果提交的Item它必須是怎么樣的一個格式,比如name是必選字段,description是可選且默認為None, price是必選,且需要是float類型的,tax是可須且默認為None。
那客戶端如何提交上面那些參數吶?
嘗試提交參數什么都不寫的情況下:
http://127.0.0.1:8000/items/
使用JSON格式提交參數的情況下:
{ "name":"Foo", "description":"An openfdsf", "price":45.4, "tax":3.5 }
故意提交錯誤參數格式請求:
{ "name":"Foo", "description":"An openfdsf", "price":"45abc", "tax":3.5 }
Request Body 和 Query 和 Path的混合
在設計一些API過程中難免的可能也會需要綜合遇到上述的一些混搭的組合,需要同時多個參數的提交和獲取
那么我們通常接收這次參數的話一般怎么接收吶?
示例代碼如:
import uvicorn from fastapi import FastAPI, Path from pydantic import BaseModel app = FastAPI() class Item(BaseModel): name: str description: str = None price: float tax: float = None @app.put("/items/{item_id}") async def update_item( *, item_id: int = Path(..., title="The ID of the item to get", ge=0, le=1000), q: str = None, item: Item = None, ): results = {"item_id": item_id} if q: results.update({"q": q}) if item: results.update({"item": item}) return results if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
通過之前的學習,其實也很簡單道理也還是一樣,如上的示例請求的話:
url:
http://127.0.0.1:8000/items/1000?q=xiao
參數:
{ "name":"Foo", "description":"An openfdsf", "price": 45.4, "tax":3.5 }
效果如下:
多個Request Body的提交
更復雜的業務其實會存在多體的Boay的提交,之前做的商城下單里面,客戶端有可能就會同時提交多個實體的對象信息到后端,如訂單實體,地址實體,商品信息實體等。
那么在Fastapi如何接受多個Body實體吶?通常以前的話,在bottle,通常直接的request.body 或 request.json就可以獲取客戶端部提交的信息了。
在Fastapi假設客戶端提交的參數是這樣的形式:
{ "item": { "name": "Foo", "description": "The pretender", "price": 42.0, "tax": 3.2 }, "user": { "username": "dave", "full_name": "Dave Grohl" } }
那如何的接收處理吶?
import uvicorn from fastapi import FastAPI, Path from pydantic import BaseModel app = FastAPI() class Item(BaseModel): name: str description: str = None price: float tax: float = None class User(BaseModel): username: str full_name: str = None @app.put("/items/{item_id}") async def update_item(*, item_id: int, item: Item, user: User): results = {"item_id": item_id, "item": item, "user": user} return results if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
這種情況,其實就是客戶端提交多個實體對象。那可以定義多個模型對象即可。fastapi它會自動幫你處理提取信息。
http://127.0.0.1:8000/items/1000
如果另外再假設:
在Fastapi假設客戶端提交的參數是這樣的形式:
{ "item": { "name": "Foo", "description": "The pretender", "price": 42.0, "tax": 3.2 }, "user": { "username": "dave", "full_name": "Dave Grohl" }, "importance": 5 }
其實這種可能也不是不存在滴,那如何的讀取解析importance參數吶?既然參數有Query 和 Path,當然也會有 Body 。
import uvicorn from fastapi import Body, FastAPI from pydantic import BaseModel app = FastAPI() class Item(BaseModel): name: str description: str = None price: float tax: float = None class User(BaseModel): username: str full_name: str = None @app.put("/items/{item_id}") async def update_item( *, item_id: int, item: Item, user: User, importance: int = Body(..., gt=0) ): results = {"item_id": item_id, "item": item, "user": user, "importance": importance} return results if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
上面的代碼中我們引入了Body 並且在importance: int = Body(...)進行處理和提取:
如果另外再假設,客戶端提交的是一個單體對象內嵌的話,我們需要怎么處理?:
{ "item": { "name": "Foo", "description": "The pretender", "price": 42.0, "tax": 3.2 } }
FastAPI提供了一個:
item: Item = Body(..., embed=True) 具體如下:
import uvicorn from fastapi import Body, FastAPI from pydantic import BaseModel app = FastAPI() class Item(BaseModel): name: str description: str = None price: float tax: float = None @app.put("/items/{item_id}") async def update_item(*, item_id: int, item: Item = Body(..., embed=True)): results = {"item_id": item_id, "item": item} return results if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
請求示例如:
如果另外再假設,客戶端提交一個更復雜的嵌套模型的話,怎么辦?麻蛋的 肯定也是會有這樣的情況滴! 嵌套里面有列表有實體。比如:
{ "name": "Foo", "description": "The pretender", "price": 42.0, "tax": 3.2, "tags": ["rock", "metal", "bar"], "image": { "url": "http://example.com/baz.jpg", "name": "The Foo live" } }
這時候,我們就需要所謂的子內嵌啦:
import uvicorn from typing import Set from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Image(BaseModel): url: str name: str class Item(BaseModel): name: str description: str = None price: float tax: float = None tags: Set[str] = [] image: Image = None @app.put("/items/{item_id}") async def update_item(*, item_id: int, item: Item): results = {"item_id": item_id, "item": item} return results if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
如上代碼,Item里面包含了Image,也包含了,tags類型的列表定義。
MMP更深層的嵌套也是可以定義的如:
{ "name":"Foo", "description":"The pretender", "price":42, "items":[ { "name":"Foo", "description":"The pretender", "price":42, "tax":3.2, "tags":[ "rock", "metal", "bar" ], "image":{ "url":"http://example.com/baz.jpg", "name":"The Foo live" } }, { "name":"Foo2", "description":"The 2", "price":422, "tax":3.2, "tags":[ "rock", "metal", "bar" ], "image":{ "url":"http://example.com/baz.jpg", "name":"The Foo live" } } ] }
對應的解析為:
import uvicorn from fastapi import FastAPI from pydantic import BaseModel from typing import List, Set app = FastAPI() class Image(BaseModel): url: str name: str class Item(BaseModel): name: str description: str = None price: float tax: float = None tags: Set[str] = [] # images: List[Image] = None image: Image = None class Offer(BaseModel): name: str description: str = None price: float items: List[Item] @app.post("/offers/") async def create_offer(*, offer: Offer): return offer if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
請求url
http://127.0.0.1:8000/offers
Request Body的Field
Field字段的意思其實就是類似上面Query, Path,也同樣給Body內的字段的信息添加相關的校驗。
也就是說。通過Field來規范提交的Body參數信息。比如:
import uvicorn from fastapi import Body, FastAPI from pydantic import BaseModel, Field app = FastAPI() class Item(BaseModel): name: str description: str = Field(None, title="標題啊", description="錯誤提示文字啊", max_length=30) price: float = Field(..., gt=0, description="錯誤提示文字啊") tax: float = None @app.put("/items/{item_id}") async def update_item(*, item_id: int, item: Item = Body(..., embed=True)): results = {"item_id": item_id, "item": item} return results if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
上面的意思就是和之前定義參數校驗其實一樣
正常情況:
{ "item":{ "name": "Foo", "description": "The pretender", "price": 42.0, "tax": 3.2 } }
異常情況:
{ "item":{ "name": "Foo", "description": "The pretender sssssssssssssssss", "price": 42.0, "tax": 3.2 } }
其他數據類型的校驗
對於數據格式的校驗,通常,我們不止於
-
int
-
float
-
str
-
bool
但是提交參數不止於上述的幾種格式,有時候比如是對手機號碼的校驗,有些時候是時間類型的校驗等
其他類型:
其他數據類型¶ 以下是您可以使用的一些其他數據類型(來自官方文檔):
-
UUID:
-
一個標准的“通用唯一標識符”,在許多數據庫和系統中常見於ID。
-
在請求和答復中,將表示為str.
-
datetime.datetime:
-
一只Pythondatetime.datetime.
-
在請求和答復中,將表示為str采用ISO 8601格式,如:2008-09-15T15:53:00+05:00.
-
datetime.date:
-
Pythondatetime.date.
-
在請求和答復中,將表示為str采用ISO 8601格式,如:2008-09-15.
-
datetime.time:
-
一只Pythondatetime.time.
-
在請求和答復中,將表示為str采用ISO 8601格式,如:14:23:55.003.
-
datetime.timedelta:
-
一只Pythondatetime.timedelta.
-
在請求和答復中,將表示為float總秒數。
-
Pydantic還允許將其表示為“ISO 8601時間差異編碼”,有關更多信息,請參閱文檔。.
-
frozenset:
-
在請求和答復中,將其視為set:
-
在請求中,將讀取列表,消除重復,並將其轉換為set.
-
在答復中,set將轉換為list.
-
生成的架構將指定set值是唯一的(使用JSONSchema的uniqueItems).
-
bytes:
-
標准Pythonbytes.
-
在請求和答復中將被視為str.
-
生成的架構將指定它是str帶着binary“格式”。
-
Decimal:
-
標准PythonDecimal.
-
在請求和響應中,處理方式與float.
所以我還可以使用其他類型來校驗:
import uvicorn from datetime import datetime, time, timedelta from uuid import UUID from fastapi import Body, FastAPI app = FastAPI() @app.put("/items/{item_id}") async def read_items( item_id: UUID, start_datetime: datetime = Body(None), end_datetime: datetime = Body(None), repeat_at: time = Body(None), process_after: timedelta = Body(None), ): start_process = start_datetime + process_after duration = end_datetime - start_process return { "item_id": item_id, "start_datetime": start_datetime, "end_datetime": end_datetime, "repeat_at": repeat_at, "process_after": process_after, "start_process": start_process, "duration": duration, } if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
本文參考鏈接:
http://www.zyiz.net/tech/detail-119883.html