媽媽再也不用擔心我不寫文件了,RESTful API文件一鍵生成!
寫在前面的小吐槽
ShichaoMa/apistellar 是我基於apistar獨立開發的WEB框架,之前也有分享過。雖然現在apistar的作者不知道吃錯了什麼藥強行把apistar閹割了,但是我的apistellar一直保持更新,絕不太監。由於apistellar在我們公司已經得到了廣泛的應用,而在公司不會多給我開一分錢的的前提下 ,我小心翼翼地維護著這個專案,畢竟一不小心就會引來bug從而遭到產品、開發、測試同學們的瘋狂責問。在此告誡大家,千萬不要把自己的專案用到公司中,公司不給你排期,不給你獎金,只會在有bug的時候找到你……,自已寫的框架,鍋都沒地甩。
吐槽完了,下面開始講正事。
背景
由於我們公司功能迭代飛快,開發排期非常吃緊,介面經常今天寫完明天就改了,所以文件一般是沒有的,有也不是最新的。所以我認為通過分析控制層的介面定義加註釋自動文件是一件非常有價值的事情,但公司不給排期不給時間(得,又開始吐槽了,我的怨氣是有多大),於是我抽了兩個週末的時間,結合apistellar自身特性,實現了API文件自動生成功能。
在實現之前,我曾嘗試藉助開源的文件生成工具來幫助解析控制層註釋,如:sphinx。但無奈需求與功能匹配度太低,sphinx的優勢在於生成類、方法介面文件。而對於RESTful API並沒有太好的支援,因而只能自己擼。
從apistellar>=1.1.0開始,apistellar開始支援生成API介面文件。通過 apistar-create document -m 要為哪個專案模組生成文件 -l 文件所以路徑 文件名稱
來為一個專案建立API文件
用法
具體命令如下:
(blog) ➜blog git:(master) ✗ apistar-create document -h usage: apistar-create document [-h] [-t TEMPLATES] -m MODULE [-l LOCATION] [-p PARSER] name positional arguments: name文件名稱 optional arguments: -h, --helpshow this help message and exit -t TEMPLATES, --templates TEMPLATES 模板路徑. -m MODULE, --module MODULE 模組地址 -l LOCATION, --location LOCATION 文章地址 -p PARSER, --parser PARSER parser模組地址
如上所示:
- -t為使用的模板路徑,一般來說使用預設模板即可。
- -m為專案包地址,如我的部落格專案,在專案根目錄,通過
pip install -e .
安裝之後,blog作為一個模組被安裝到了python可搜尋路徑下,所以-m blog
就可以指定為其生成文件。 - -l為生成的文件所在路徑。
- -p為controller解析器類對應的路徑,通過
module:class
的形式指定,一般來說使用預設解析器即可。
展示
在建立完文件之後,我們可以在指定文件路徑下找到以 name
命名的文件資料夾, 如 blog-api文件 以每個controller為單位各自生成一個文件資料夾及文件檔案,md和html格式各一份。同時還會在根目錄生成一個index檔案,用來索引文件。index.html會被自動開啟,接下來我們就可以瀏覽文件了。
文件組成
接下來我們將以 blog-api文件 為例來講解文件的語法及生成規範。
apistellar專案API文件主要分主兩大部分:模型定義和介面定義

這部分主要聲明瞭文件介面中可能會引用到的模型,這部分詳細說明了模型的定義。其資料主要來源於該模型的各種欄位引數和註釋。程式碼中的模型是如下定義的:
class Article(PersistentType, SqliteDriverMixin, SettingsMixin): """ 文章模型 :param title: 標題 :ex `我的主頁` :param id: 每篇文章的唯一id,日期字串的形式表示 :param tags: 關鍵字 :ex tags: `["python", "apistellar"]` :param description: 描述資訊 :param author: 作者資訊 :param feature: 是否為精品 :ex feature: True/False :param updated_at: 更新時間 :param created_at: 建立時間 :param show: 是否在文章列表中展示 :ex show: True/False :param article: 文章正文 """ TABLE = "articles" title = validators.String() id = validators.String(default=get_id) tags = Tags() description = validators.String(default="") author = validators.String(default=settings_wrapper.settings["AUTHOR"]) feature = Boolean(default=False) created_at = Timestamp(default=datetime.datetime.now().timestamp) updated_at = Timestamp(default=datetime.datetime.now().timestamp) show = Boolean(default=True) article = validators.String(default=str)
:param
樣式為rst標準註釋規範,使用pycharm會預設生成該樣式的註釋。而 :ex
樣式為我定製的擴充套件註釋,其後通過markdown語法反引號來指定一段示例。上述註釋是可以省略的,但不推薦,完善的註釋才能生成規範的文件。 需要注意的一點是,當欄位的default屬性等於一個工廠函式時,在生成文件時預設值一欄會被填充為工廠的返回值。
介面定義
介面定義部分描述了每個介面的具體資訊,其中包括url、method、查詢引數、路徑引數、表單引數、json請求體、返回資訊、返回響應碼等模組。url和method一目瞭然,下面將分別介紹以上模組。
查詢引數
查詢引數是指一次請求,URL ?
號後面的引數如?a=1&b=2,通過在handler中指定 http.QueryParam
或 http.QueryParams
型別,來自動生成查詢引數表格。如:
@get("/") def index(self, app: App, path: QueryParam) -> str: """ 首頁 :param app: :param path: 子路徑 :ex path: `"/article?a=3"`
上述handler在定義時指定了一個查詢引數path,文件生成器會解析上述引數列表及註釋生成如下文件:

而當handler在定義時使用了 http.QueryParams
時,如:
@post("/a/{+path}") def test(self, b: http.QueryParams, path: str): """ 測試 :param b: 測試QueryParams :ex b: ```json {"a": 1, "b": 2} ``` """
文件生成器會解析註釋中的json,將其分解成兩個引數並分別描述其性質。如:

路徑引數
路徑引數決定了handler將從URL中提取部分路徑作為引數。如:
@post("/a/{+path}") def test(self, path: str): """ 測試 :param path: 傳個地址
文件生成器會生成如下文件:

表單引數
表單引數為form中的引數,其可通過指定 apistellar.FormParam
、 apistellar.FileStream
的形式接參。如:
@post("/a/{+path}") def test(self, name: FormParam): """ 測試 :param name: 輸入名字 :ex name: `abcd`
其渲染出來的文件如下:

我們可能在上傳檔案時會使用 apistellar.FileStream
元件,如:
@post("/test/b") def test(self, stream: FileStream): """ :param stream: 這一個檔案流 :return: """
其渲染出來的文件如下:

模型是可以用於form接參的,如:
@post("/check") async def check(self, article: Article): """ 檢查使用者名稱和密碼是否正確 :param article: :ex article: ```json {"title": "xxx"} ``` :type article: form
其渲染出來的文件如下:

注意,由於模型的接參機制是針對所有請求體的,所以 Content-Type=application/json
的請求也會被處理,因此 :type article: form
必須指定。否則,文件會被被識別為json請求體。
json請求體
對於主打RESTful API WEB程式開發的apistellar,post json請求體的情況應該是最常見的,如:
@post("/a/{+path}") def test(self, data: http.RequestData): """ 測試 :param data: post過來的引數集合 :ex data: ```json {"a": 1} ``` :ex data: ```json {"ab": 1} ``` """
其渲染出來的文件如下:

與上節類似,模型是可以用於json接參的,如:
@post("/a/{+path}") def test(self, data: Article): """ 測試 :param data: post過來的引數集合 """
渲染出的來的文件如下:

我們可以從模型定義部分找到相應的模型來了解模型約束。
返回資訊
通過返回型別及註釋可以生成用於描述返回資訊的文件,如:
@post("/a/{+path}") @return_wrapped(error_info={1: "錯誤1", 2: "錯誤2"}) def test(self, data: Article) -> typing.List[Article]: """ 測試 :param data: post過來的引數集合 :ex data: ```json {"a": 1} ``` :ex data: ```json {"ab": 1} ``` :return: ```json {"code": 0, "data": {"a": 1}} ``` """ return [data]
通過 :return:
來定義返回示例, -> typing.List[Article]
指明瞭返回型別, @return_wrapped()
存在的意義在於為返回值增加一層響應碼資訊,預設增加的格式為 {"code": 0, "data": 返回值}
,通過關鍵字引數 success_key_name
可以改變返回值的key名稱 data
,通過關鍵字引數 success_code
可以改變成功時的響應碼 0
,同時裝飾器還支援 error_info
關鍵字引數,其指向一個異常響應碼和異常資訊字典,其存在的意義僅是用來生成返回碼資訊。下面是一個返回資訊文件示例:

配合簡單的docstring和型別註解,一個資訊完整的RESTful AP文件就被渲染了出來。
如果感興趣的話,大家可以嘗試使用apistellar開發一個小專案練練手哦!來體驗一下非同步程式設計的樂趣吧!
ShichaoMa/apistellar github.com
下次分享一下一鍵生成RPC呼叫,畢竟文件都有了,生成出PRC client也只不過是分分鐘事情!