flask-siwadoc:一個(gè)自動(dòng)生成openapi接口文檔的庫(kù)
在前后端分離變得越來(lái)越流行的今天,大家的職責(zé)也更加明確與專注,后端負(fù)責(zé)提供API接口,前端負(fù)責(zé)UI與交互。前后端基于接口文檔進(jìn)行溝通。
但是程序員最討厭兩件事,一是討厭自己寫文檔,另一件是討厭別人不寫文檔。
所以對(duì)后端來(lái)說(shuō),理想情況是代碼寫完,文檔已經(jīng)自動(dòng)生成好了。openapi(就是早前的swagger)已成為事實(shí)上的接口文檔規(guī)范指南,既然都有規(guī)范出現(xiàn),那么基于這份規(guī)范來(lái)實(shí)現(xiàn)一個(gè)自動(dòng)生成的openapi的接口文檔庫(kù)就不是難事了。
所以我就寫了這樣一個(gè)庫(kù)
名字叫flask-siwadoc ,它是一個(gè)兼具 數(shù)據(jù)校驗(yàn)和openapi(swagger)文檔自動(dòng)生成的項(xiàng)目。這兩個(gè)特性都是寫restful接口剛需痛點(diǎn)功能。
現(xiàn)來(lái)說(shuō)說(shuō)這個(gè)庫(kù)的一些特點(diǎn):
0、零配置
接入flask-siwadoc無(wú)需任何配置, 只需要?jiǎng)?chuàng)建一個(gè)SiWaDoc實(shí)例,即可使用,沒有復(fù)雜的配置。當(dāng)然,在后面的版本,會(huì)開放相關(guān)的配置接口得以讓開發(fā)者根據(jù)自己的需求來(lái)配置openapi。
1、數(shù)據(jù)校驗(yàn)
flask-siwadoc 站在巨人肩膀上,數(shù)據(jù)校驗(yàn)利用pydantic強(qiáng)大的數(shù)據(jù)驗(yàn)證功能,支持請(qǐng)求查詢參數(shù)和請(qǐng)求體參數(shù)的數(shù)據(jù)校驗(yàn)及轉(zhuǎn)換功能。因此本項(xiàng)目同時(shí)依賴于pydantic。
2、接口文檔自動(dòng)生成
接口文檔生成只需要簡(jiǎn)單初始化一個(gè)siwa=SiwaDoc(app),利用裝飾器 siwa.doc()修飾flask的視圖函數(shù),即可將該視圖函數(shù)加入openapi的接口文檔規(guī)范中。
3、ui切換
flask-siwadoc內(nèi)置了redoc、swagger、rapidoc等多種UI界面,通過(guò)參數(shù)/docs/?ui=xxx切換
4、文檔支持分組與標(biāo)簽
對(duì)于大型項(xiàng)目,接口分組和標(biāo)簽顯得尤為重要,不然前端很難找到相關(guān)接口。
快速開始
安裝
pip?install?flask-siwadoc
現(xiàn)在來(lái)看個(gè)簡(jiǎn)單例子:
example 1
初始化SiwaDoc支持兩種方式,一種直接將Flask實(shí)例傳入SiwaDoc的構(gòu)造方法,另一種是使用工廠模式進(jìn)行初始化。
from?flask?import?Flask
from?flask_siwadoc?import?SiwaDoc
app?=?Flask(__name__)
siwa?=?SiwaDoc(app)
#?or
#?siwa?=?SiwaDoc()
#?siwa.init_app(app)
@app.route("/hello",?methods=["GET"])
@siwa.doc()
def?hello():
????return?"hello?siwadoc"
if?__name__?==?'__main__':
????app.run()
運(yùn)行后,訪問(wèn) http://127.0.0.1:5000/docs 就可以看到該接口的文檔頁(yè)面

對(duì)于大部分接口來(lái)說(shuō),我們要對(duì)請(qǐng)求參數(shù)進(jìn)行校驗(yàn),有的參數(shù)來(lái)源于url的查詢參數(shù),有的是以application/json格式作為請(qǐng)求體的參數(shù)。我們只需要基于Pydantic定義好對(duì)應(yīng)的Model數(shù)據(jù)結(jié)構(gòu),該結(jié)構(gòu)一方面可以用來(lái)做數(shù)據(jù)校驗(yàn),另一方面可用于生成openapi所需的schema。
example 2:指定查詢參數(shù) query
from?pydantic?import?BaseModel,?Field
class?QueryModel(BaseModel):
????page:?int?=?Field(default=1,?title="current?page?number")
????size:?int?=?Field(default=20,?title="size?of?page",?ge=10,?le=100)
@app.route("/users",?methods=["GET"])
@siwa.doc(query=QueryModel,?tags=['user'])
def?users_list():
????"""
????user?list
????"""
????return?[{"username":?"siwa",?"id":?1}]
對(duì)于查詢接口,GET請(qǐng)求需如果有查詢參數(shù),得益于pydantic強(qiáng)大的類型功能,我們只需要一個(gè)繼承BaseModel的自定義類,即可實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)及轉(zhuǎn)換。
如何在視圖函數(shù)中使用該query這個(gè)對(duì)象呢?有兩種方式
方式一:
@app.route("/users",?methods=["GET"])
@siwa.doc(query=QueryModel,?tags=["user"])
def?hello():
????print(request.query.keyword)
????return?"hello"
flask-siwadoc 將QueryModel的實(shí)例對(duì)象query綁定到了flask的request對(duì)象上,不過(guò)對(duì)開發(fā)者來(lái)說(shuō)使用并不友好,從代碼上你沒法知道他是什么類型么,IDE也無(wú)法用.的方式調(diào)出該實(shí)例的屬性。你只有看了flask-siwadoc的文檔或者源碼才知道是怎么回事。
方式二:(推薦方式)
@app.route("/users",?methods=["GET"])
@siwa.doc(query=QueryModel,?tags=["user"])
def?hello(query:?QueryModel):
????print(query.keyword)
????return?"hello"
將query變量作為視圖函數(shù)的參數(shù),flask-siwadoc 會(huì)自動(dòng)將QueryModel實(shí)例對(duì)象注入到該函數(shù)一個(gè)叫query的變量中,我們可以給query指定類型聲明,因此通過(guò)IDE可以很方便的調(diào)出實(shí)例屬性。開發(fā)者一眼也能看出他的類型。
下面的example3中的body參數(shù)原理也是類似的。

example3 :指定請(qǐng)求體 body
class?LoginModel(BaseModel):
????username:?str
????password:?str
@app.route("/login",?methods=["POST"])
@siwa.doc(body=LoginModel)
def?login(body:?LoginModel):
????return?{"username":?body.username,?"id":?1}
對(duì)于POST請(qǐng)求,請(qǐng)求體一般是基于application/json 類型, flask-siwadoc會(huì)自動(dòng)將數(shù)據(jù)轉(zhuǎn)換成LoginModel類型的對(duì)象,就可以像靜態(tài)語(yǔ)言一樣,通過(guò)點(diǎn)的方式調(diào)出屬性,例如 body.username

example4: 指定返回體 resp
對(duì)于接口文檔來(lái)說(shuō),除了要告訴前端需要傳什么參數(shù)之外,還需要告訴他們返回的結(jié)構(gòu)是什么,同樣也只需要定義一個(gè)Model,寫明有什么字段,字段對(duì)應(yīng)的類型是什么即可。在doc裝飾器中賦值給resp變量。
class?UserModel(BaseModel):
????id:?int
????username:?str
@app.route("/users/1",?methods=["GET"])
@siwa.doc(resp=UserModel)
def?users():
????"""
????user?detail
????"""
????return?{"username":?"siwa",?"id":?1}

example5: 指定標(biāo)簽分類 tags
項(xiàng)目中如果接口太多,我們可以對(duì)接口根據(jù)業(yè)務(wù)劃分不同的模塊標(biāo)簽來(lái)分類管理,我們只需要在裝飾器 siwa.doc 指定tags參數(shù)
@siwa.doc(resp=UserModel,?tags=["user"])
...
指定tags參數(shù),tags參數(shù)是一個(gè)列表,一個(gè)接口可支持多個(gè)標(biāo)簽。

example6:路徑參數(shù)也支持文檔化
除了查詢參數(shù)和請(qǐng)求體參數(shù)外,對(duì)于url路徑中的參數(shù),例如/users/,這是flask的路由語(yǔ)法,指users后面是一個(gè)必須大于1的整數(shù),生成文檔時(shí),不需要開發(fā)者做額外的處理,flask-siwadoc內(nèi)部經(jīng)過(guò)處理,直接將參數(shù)反映在接口文檔中。
class?QueryModel(BaseModel):
????gender:?str
class?UpdatePasswordModel(BaseModel):
????password:?str
@app.route("/users/",?methods=["POST"])
@siwa.doc(query=QueryModel,?body=UpdatePasswordModel,?resp=UserModel)
def?update_password(user_id):
????"""
????update?password
????"""
????return?{"username":?"siwa",?"id":?user_id}

完整示例可參考example.py
UI切換
文檔默認(rèn)使用redoc進(jìn)行渲染,你可以通過(guò)指定參數(shù)ui=swaggerui顯式文檔。
http:?//?127.0
.0
.1:?5000?/?docs?/?ui?=?swagger

擴(kuò)展
如果數(shù)據(jù)校驗(yàn)報(bào)錯(cuò),flask-siwadoc 會(huì)拋出異常flask_siwadoc.error.ValidationError,ValidationError異常繼承自pydantic.ValidationError
例如:
class?QueryModel(BaseModel):
????keyword:?str
@app.route("/users",?methods=["GET"])
@siwa.doc(query=QueryModel,?tags=["user"])
def?hello(query:?QueryModel):
????print(query)
????return?"hello"
該接口中,keyword是必選的查詢參數(shù),如果url中沒有keyword參數(shù),就會(huì)拋出異常
????raise?ValidationError(e)
flask_siwadoc.error.ValidationError:?2?validation?errors?for?Auth
????username
field?required?(type=value_error.missing)
password
field?required?(type=value_error.missing)
這時(shí)候,我們可以通過(guò)使用flask的 errorhandler() 裝飾函數(shù)來(lái)注冊(cè)ValidationError錯(cuò)誤,異常就可以被validate_error函數(shù)捕獲,開發(fā)者可以給前端直接返回一個(gè)友好的錯(cuò)誤響應(yīng)體
@app.errorhandler(ValidationError)
def?validate_error(e:?ValidationError):
????return?dict(code=-1,?msg="請(qǐng)求參數(shù)錯(cuò)誤",?error_info=e.errors()),?400

github地址:https://github.com/lzjun567/flask-siwadoc
任何問(wèn)題歡迎發(fā)issue或者加我微信 lzjun567 交流,歡迎PR
