天天看點

flask-openapi3

是什麼

flask-openapi3
flask-openapi3
flask-openapi3
flask-openapi3

flask-openapi3是一個基于Flask的WEB API架構,設計靈感來自于FastAPI,使用pydantic驗證資料,自動生成Swagger UI和Redoc兩種線上API文檔。

flask-openapi3
flask-openapi3
flask-openapi3

為什麼

python WEB已經有很多成熟的知名架構,Flask、Django、FastAPI…,為什麼還要自己開發一個呢?首先我是一個熱衷于Flask後端開發的愛好者,它是一個那麼簡單、優雅、python範兒的一個輕量級架構,各種插件:REST、資料庫、表單驗證、swagger文檔…,在REST API開發過程中,我遇到了一些問題:

  1. REST API插件有flask-restful和flask-restplus,其中flask-restplus支援swagger文檔生成和表單驗證,但基于Resource的API設計對REST執行的過于嚴格,顯得不是那麼靈活。
  2. 最知名的swagger文檔插件非flasgger莫屬了,但它很強的代碼侵入性給文檔編寫和代碼閱讀造成了一定的影響,不可否認的是在這之前我的項目中就是使用的flasgger。
  3. 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)

           

項目位址

源碼位址:點選這裡

同時也提供一個完成的項目示例

繼續閱讀