什麼是FastAPI非同步框架?(全面瞭解)

2022-07-21 21:01:04

一:FastAPI框架

1.FastAPI是應該用於構建API的現代,快速(高效能)的 web 框架,使用Python 3.6+ 並基於標準的 Python 型別提示。

關鍵性:

  • 快速: 可與NodeJS和Go並肩的極高效能(歸功於Starlette和Pydantic)。最快的Python web框架之一。
  • 高效編碼: 提高功能開發速度約200% 至 300%。
  • 更少的bug: 減少約 40% 的人為(開發者) 導致錯誤。
  • 智慧: 極佳的編輯器支援。處處皆可自動補全,減少調式時間。
  • 簡單: 設計的易於使用和學習,閱讀檔案的時間。
  • 簡短: 使程式碼重複最小化。通過不同的引數宣告實現豐富功能。bug更少。
  • 健壯: 生產可用級別的程式碼。還有自動生成的互動式檔案。
  • 標準化: 基於(並完全相容)API的相關開放標準: OpenAPI(以前被稱為 Swagger)和 JSON Schema。

1.Starlette,Pydantic 與 FastAPI 的關係

  • Python 的型別提示 type hints
  • Pydantic 是一個基於 Python 型別提示來定義資料驗證,序列化和檔案(使用JSON模式)庫
Pydantic : 前端給後端傳資料/定義的資料型別,巢狀用什麼格式巢狀
  • Starlette 是一種輕量級的 ASGI 框架/工具包,是構建高效能Asyncio 服務的理想選擇。
  • 一個輕量級、低複雜度的 HTTP Web 框架。
  • WebSocket 支援。
  • 正在進行的後臺任務。
  • 啟動和關閉事件。
  • 測試使用者端建立在requests.
  • CORS、GZip、靜態檔案、流響應。
  • 對談和 Cookie 支援。
  • 100% 的測試覆蓋率。
  • 100% 型別註釋程式碼庫。
  • 很少有硬依賴。
  • asynciotrio後端相容。
  • 與獨立基準相比,整體表現出色。

2.ASGI 和 WSGI的區別

ASGI 協定
WSGI 協定

Uvicorn 與 uWSGI 為web伺服器

二:Pydantic的基本用法(BaseModel)

1.Pydantic的基本用法

class User(BaseModel):
    id: int  # 沒有預設值。就是必填欄位
    name: str = "john Snow"  # 有預設值,就是選填欄位
    signup_ts: Optional[datetime] = None  # 時間 "有預設值,選填欄位"
    friends: List[int] = []  #  列表中元素是int型別或者可以直接轉換成int型別

# 傳值 型別= 字典
external_data = {
    "id": "123",
    "signup_ts": "2022-12-22 12:22",
    "friends": [1, 2, "3"]  # "3" 是可以int("3")的
}

user = User(**external_data)
print(user.id, user.friends)  # 範例化後呼叫屬性
print(repr(user.signup_ts))
print(user.dict())

"
123 [1, 2, 3]
datetime.datetime(2022, 12, 22, 12, 22)
{'id': 123, 'name': 'john Snow', 'signup_ts': datetime.datetime(2022, 12, 22, 12, 22), 'friends': [1, 2, 3]}
"
2.效驗失敗處理
print("---效驗失敗處理---")
try:
    User(id=1, signup_ts=datetime.today(), friends=[1, 2, "not number"])
except ValidationError as e:
    print(e.json())

"
[
  {
    "loc": [
      "friends",
      2
    ],
    "msg": "value is not a valid integer",
    "type": "type_error.integer"
  }
]
"
3.模型類的屬性和方法
print(user.dict())  # 轉換成字典
print(user.json()) 
print(user.copy())  # 這裡代表淺拷貝
print(User.parse_obj(obj=external_data))  # 解析
print(User.parse_raw('{"id": "123","signup_ts": "2022-12-22 12:22", "friends": [1, 2, 3]}'))

"
{'id': 123, 'name': 'john Snow', 'signup_ts': datetime.datetime(2022, 12, 22, 12, 22), 'friends': [1, 2, 3]}
{"id": 123, "name": "john Snow", "signup_ts": "2022-12-22T12:22:00", "friends": [1, 2, 3]}
id=123 name='john Snow' signup_ts=datetime.datetime(2022, 12, 22, 12, 22) friends=[1, 2, 3]
id=123 name='john Snow' signup_ts=datetime.datetime(2022, 12, 22, 12, 22) friends=[1, 2, 3]
id=123 name='john Snow' signup_ts=datetime.datetime(2022, 12, 22, 12, 22) friends=[1, 2, 3]
"
4.解析檔案
path = Path('pydantic_tutorial.json')
path.write_text('{"id":"123", "signup_ts":"2020-12-22 12:22", "friends":[1, 2, "3"]}')
print(User.parse_file(path))

"
id=123 name='john Snow' signup_ts=datetime.datetime(2020, 12, 22, 12, 22) friends=[1, 2, 3]
"
5.解析(並列印出對應型別)
print(user.schema())
print(user.schema_json())
#
user_data = {"id": "error", "signup_ts": "2020-12-22 12 22", "friends": [1, 2, 3]}  # id必須是str
print(User.construct(**user_data))  # 不效驗資料直接建立模型類,不建議在construct方法中傳入未經驗證的資料
#
print(User.__fields__.keys())  # 定義模型類的時候,所有欄位都註明型別,欄位順序就不會亂。


"
{'title': 'User', 'type': 'object', 'properties': {'id': {'title': 'Id', 'type': 'integer'}, 'name': {'title': 'Name', 'default': 'john Snow', 'type': 'string'}, 'signup_ts': {'title': 'Signup Ts', 'type': 'string', 'format': 'date-time'}, 'friends': {'title': 'Friends', 'default': [], 'type': 'array', 'items': {'type': 'integer'}}}, 'required': ['id']}
{"title": "User", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "name": {"title": "Name", "default": "john Snow", "type": "string"}, "signup_ts": {"title": "Signup Ts", "type": "string", "format": "date-time"}, "friends": {"title": "Friends", "default": [], "type": "array", "items": {"type": "integer"}}}, "required": ["id"]}
name='john Snow' signup_ts='2020-12-22 12 22' friends=[1, 2, 3] id='error'
dict_keys(['id', 'name', 'signup_ts', 'friends'])
"
6.遞迴模型
class Sound(BaseModel):
    sound: str
class Dog(BaseModel):
    birthday: date
    weight: float = Optional[None]
    sound: List[Sound]  # 不同的狗有不同的叫聲。遞迴模型(Recursive Model)就是指一個巢狀一個
dogs = Dog(birthday=date.today(), weight=6.66, sound=[{"sound": "wang wang ~"}, {"sound": "ying ying ~"}])
print(dogs.dict())


"
{'birthday': datetime.date(2022, 6, 9), 'sound': [{'sound': 'wang wang ~'}, {'sound': 'ying ying ~'}]}
"
7.ORM模型:從類範例建立符合ORM物件的模型
from sqlalchemy import Column, Integer, String
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.ext.declarative import declarative_base


Base = declarative_base()

class CompanyOrm(Base):
    __tablename__ = 'companies'  # 表名
    id = Column(Integer, primary_key=True, nullable=False)
    public_key = Column(String(20), index=True, nullable=False, unique=True)
    name = Column(String(63), unique=True)
    domains = Column(ARRAY(String(255)))

class CompanyMode(BaseModel):
    id: int
    public_key: constr(max_length=20)   # constr = 限制
    name: constr(max_length=63)
    domains: List[constr(max_length=255)]

    class Config:  # 子類
        orm_mode = True

co_orm = CompanyOrm(
    id=123,
    public_key = 'foobar',
    name='Testing',
    domains=['example.com', 'imooc.com']
)
print(CompanyMode.from_orm(co_orm))


"
id=123 public_key='foobar' name='Testing' domains=['example.com', 'imooc.com']
"