是什麼
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyMpBXYuVGcv1yazFGbm9CX29CXpBXew9CXvlmLzRGbllGaz5yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
flask-openapi3是一個基于Flask的WEB API架構,設計靈感來自于FastAPI,使用pydantic驗證資料,自動生成Swagger UI和Redoc兩種線上API文檔。
為什麼
python WEB已經有很多成熟的知名架構,Flask、Django、FastAPI…,為什麼還要自己開發一個呢?首先我是一個熱衷于Flask後端開發的愛好者,它是一個那麼簡單、優雅、python範兒的一個輕量級架構,各種插件:REST、資料庫、表單驗證、swagger文檔…,在REST API開發過程中,我遇到了一些問題:
- REST API插件有flask-restful和flask-restplus,其中flask-restplus支援swagger文檔生成和表單驗證,但基于Resource的API設計對REST執行的過于嚴格,顯得不是那麼靈活。
- 最知名的swagger文檔插件非flasgger莫屬了,但它很強的代碼侵入性給文檔編寫和代碼閱讀造成了一定的影響,不可否認的是在這之前我的項目中就是使用的flasgger。
- WTForms很好的解決了表單驗證的問題,但對json資料的解析卻不是那麼簡單,當然你可以重構其表單解析和驗證機制。
歸于以上原因,我決定開始flask-openapi3。
怎麼用
像Flask一樣
你可以像使用Flask一樣來使用,當然你可能并不想這麼做。
from flask_openapi3 import OpenAPI
app = OpenAPI(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run()
一個簡單的例子
Flask 2.x已經實作了REST API的編寫方式,像
app.get
、
app.post
、
app.put
、
app.delete
,但是它僅僅是
app.route
的快捷方式,參考此設計這裡實作了
OpenAPI
,它能接收
info
參數——生成swagger文檔的必要資訊,并給每一個API提供一個
tags
參數(給API分類),更進一步支援
security
(安全驗證)、
responses
(響應體驗證)等參數。
為視圖函數設計了
path
、
query
、
form
、
body
、
header
、
cookie
六個參數,來支援不同類型的參數——強大的
pydantic
庫為參數驗證提供了有力的支撐。
from pydantic import BaseModel
from flask_openapi3 import OpenAPI
from flask_openapi3.models import Info, Tag
info = Info(title='book API', version='1.0.0')
app = OpenAPI(__name__, info=info)
book_tag = Tag(name='book', description='圖書')
class BookData(BaseModel):
age: int
author: str
@app.get('/book', tags=[book_tag])
def get_book(query: BookData):
"""get books
get all books
"""
return {
"code": 0,
"message": "ok",
"data": [
{"bid": 1, "age": query.age, "author": query.author},
{"bid": 2, "age": query.age, "author": query.author}
]
}
if __name__ == '__main__':
app.run(debug=True)
REST API
接下來展示一個完整的REST API例子:
from typing import Optional
from pydantic import BaseModel, Field
from flask_openapi3 import OpenAPI
from flask_openapi3.models import Info, Tag
from flask_openapi3.models.security import HTTPBearer
info = Info(title='book API', version='1.0.0')
securitySchemes = {"jwt": HTTPBearer(bearerFormat="JWT")}
app = OpenAPI(__name__, info=info, securitySchemes=securitySchemes)
book_tag = Tag(name='book', description='圖書')
security = [{"jwt": []}]
class Path(BaseModel):
bid: int = Field(..., description='圖書id')
class BookData(BaseModel):
age: Optional[int] = Field(..., ge=2, le=4, description='年齡')
author: str = Field(None, min_length=2, max_length=4, description='作者')
class BookDataWithID(BaseModel):
bid: int = Field(..., description='圖書id')
age: Optional[int] = Field(None, ge=2, le=4, description='年齡')
author: str = Field(None, min_length=2, max_length=4, description='作者')
class BookResponse(BaseModel):
code: int = Field(0, description="狀态碼")
message: str = Field("ok", description="異常資訊")
data: BookDataWithID
@app.get('/book/<int:bid>', tags=[book_tag], responses={"200": BookResponse}, security=security)
def get_book(path: Path, query: BookData):
"""擷取圖書
根據圖書id擷取圖書
"""
return {"code": 0, "message": "ok", "data": {"bid": path.bid, "age": query.age, "author": query.author}}, 522
@app.get('/book', tags=[book_tag])
def get_books(query: BookData):
"""get books
get all books
"""
return {
"code": 0,
"message": "ok",
"data": [
{"bid": 1, "age": query.age, "author": query.author},
{"bid": 2, "age": query.age, "author": query.author}
]
}
@app.post('/book', tags=[book_tag])
def create_book(body: BookData):
print(body)
return {"code": 0, "message": "ok"}
@app.put('/book/<int:bid>', tags=[book_tag])
def update_book(path: Path, body: BookData):
print(path)
print(body)
return {"code": 0, "message": "ok"}
@app.delete('/book/<int:bid>', tags=[book_tag])
def delete_book(path: Path):
print(path)
return {"code": 0, "message": "ok"}
if __name__ == '__main__':
app.run(debug=True)
項目位址
源碼位址:點選這裡
同時也提供一個完成的項目示例