成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Flask Api 文檔管理與 Swagger 上手

Scholer / 3663人閱讀

摘要:眾數(shù)周知,文檔的編寫和整理工作將花費巨大精力甚至不亞于代碼的編寫,因此在時間緊任務(wù)重的情況下,文檔是首先被忽略的工作。是一款非常流行的文檔管理交互工具,適用于在團隊中的管理,以及服務(wù)組件對接。而我們目前需要的是獲取文檔或文件。

本文最先發(fā)布在博客:https://blog.ihypo.net/152551...

Flask 是一個以自由度高、靈活性強著稱的 Python Web 框架。但高靈活性也意味著無盡的代碼維護成本、高自由度意味著代碼質(zhì)量更依賴程序員自身而沒有一致的標準和規(guī)范。因此團隊內(nèi)開發(fā)時 Flask 項目更需要建立代碼和文檔規(guī)范以保證不會出現(xiàn)太大的偏差。

本文從 Api 的角度探究 Flask 項目的 Api 規(guī)范以及獲得 Api 文檔的最佳姿勢。眾數(shù)周知,文檔的編寫和整理工作將花費巨大精力甚至不亞于代碼的編寫,因此在時間緊任務(wù)重的情況下,文檔是首先被忽略的工作。不過,就算項目在初期存在文檔,但在后面的迭代中,文檔落后嚴重,其產(chǎn)生的誤導比沒有文檔更加可怕。

因此,個人認為 文檔隨代碼走,代碼改動時文檔也應(yīng)該跟進變動,但本著 人是不可靠的 原則,文檔理想上是應(yīng)該由代碼生成,而不是靠人工維護。如果代碼有任何改動,文檔也能自動更新,這將是一件非常優(yōu)雅的事情。雖然對很多文檔來說這并不現(xiàn)實,但對于 Api 文檔來說,實現(xiàn)成本并不高。

Flask-RESTPlus

對于 REST Api 來說,Flask-RESTPlus 是一個優(yōu)秀的 Api 文檔生成工具,這個包將會替換 Flask 路由層的編寫方式,通過自己的語法來規(guī)定 Api 細節(jié),并生成 Api 文檔。

安裝

安裝 Flask-RESTPlus

pip install flask-restplus

或者:

easy_install flask-restplus
最小 Demo

使用 Flask-RESTPlus 時需要按照這個庫規(guī)定的方式編寫 Api 層,包括 request 的參數(shù)解析,以及 response 的返回格式。一個 hello world 級的示范:

from flask import Flask
from flask_restplus import Resource, Api

app = Flask(__name__)
api = Api(app, prefix="/v1", title="Users", description="Users CURD api.")

@api.route("/users")
class UserApi(Resource):
    def get(self):
        return {"user": "1"}

if __name__ == "__main__":
    app.run()

運行之后效果如下:

實踐

這里我會實現(xiàn)一個完整的小項目來實踐和介紹 Flask-RESTPlus 這個庫。我們實現(xiàn)一個簡單的 圖書訂單系統(tǒng) ,實現(xiàn)用戶、圖書和訂單的 CURD。

Model

用戶 model,包含 id 和 username:

class User(object):
    user_id = None
    username = None

    def __init__(self, username: str):
        self.user_id = str(uuid.uuid4())
        self.username = username

圖書 model,包含 id,名稱和價格:

class Book(object):
    book_id = None
    book_name = None
    price = None

    def __init__(self, book_name: str, book_price: float):
        self.book_id = str(uuid.uuid4())
        self.book_name = book_name
        self.price = book_price

訂單 model,包含 id,購買者 id,圖書 id 和創(chuàng)建時間:

class Order(object):
    order_id = None
    user_id = None
    book_id = None
    created_at = None

    def __init__(self, user_id, book_id):
        self.order_id = str(uuid.uuid4())
        self.user_id = user_id
        self.book_id = book_id
        self.created_at = int(time.time())
藍圖

在 Flask 中構(gòu)建大型 Web 項目,可以通過藍圖為路由分組,并在藍圖中添加通用的規(guī)則(url 前綴、靜態(tài)文件路徑、模板路徑等)。這個項目我們只用一個 api 藍圖,在實際中可能會使用 openapi 藍圖,internal api 藍圖來區(qū)分大的分類。

Flask-RESTPlusclass::Api 將直接掛在在藍圖下面,這么我們即利用了 Flask 的藍圖進行對功能模塊分類,也可以利用 Api 的版本對 Api 版本進行管理,對于小的模塊分類,我們可以利用 Api 的 namespace,著這里我們可以分為 user namespace,book namespaceorder namespace:

Api 藍圖:

from flask import Blueprint
from flask_restplus import Api

api_blueprint = Blueprint("open_api", __name__, url_prefix="/api")
api = Api(api_blueprint, version="1.0",
          prefix="/v1", title="OpenApi", description="The Open Api Service")

然后,就可以創(chuàng)建出不同的 namespace,來編寫自己的 api 代碼了。而只需要在 app 工廠中注冊該 blueprint,便可將自己的編寫的 api 掛載到 flask app 中。

def create_app():
    app = Flask("Flask-Web-Demo")

    # register api namespace
    register_api()

    # register blueprint
    from apis import api_blueprint
    app.register_blueprint(api_blueprint)

    return app

要注意的是,因為 Api 中很多工具方法依賴 api 對象,因此在注冊 namespace 的時候要避免循環(huán)引用,而且,這注冊藍圖的時候,需要先將 namespace 注冊,否則會 404。這個庫的很多方法太依賴 api 對象,感覺設(shè)計并不合理,很容易就循環(huán)引用,并不是非常優(yōu)雅。

注冊 namespace:

def register_api():
    from apis.user_api import ns as user_api
    from apis.book_api import ns as book_api
    from apis.order_api import ns as order_api
    from apis import api
    api.add_namespace(user_api)
    api.add_namespace(book_api)
    api.add_namespace(order_api)

下面就是 Api 的編寫了。

編寫 Api 列表和創(chuàng)建

我們先完成用戶的列表和創(chuàng)建 Api,代碼如下:

from flask_restplus import Resource, fields, Namespace

from model import User
from apis import api

ns = Namespace("users", description="Users CURD api.")

user_model = ns.model("UserModel", {
    "user_id": fields.String(readOnly=True, description="The user unique identifier"),
    "username": fields.String(required=True, description="The user nickname"),
})
user_list_model = ns.model("UserListModel", {
    "users": fields.List(fields.Nested(user_model)),
    "total": fields.Integer,
})


@ns.route("")
class UserListApi(Resource):
    # 初始化數(shù)據(jù)
    users = [User("HanMeiMei"), User("LiLei")]

    @ns.doc("get_user_list")
    @ns.marshal_with(user_list_model)
    def get(self):
        return {
            "users": self.users,
            "total": len(self.users),
        }

    @ns.doc("create_user")
    @ns.expect(user_model)
    @ns.marshal_with(user_model, code=201)
    def post(self):
        user = User(api.payload["username"])
        return user

解釋下上面的代碼,首先需要創(chuàng)建一個 user model 來讓 Flask-RESTPlus 知道我們?nèi)绾武秩竞徒馕?json:

user_model = ns.model("UserModel", {
    "user_id": fields.String(readOnly=True, description="The user unique identifier"),
    "username": fields.String(required=True, description="The user nickname"),
})

這里面定義了字段以及字段的描述,這些字段并不參與參數(shù)檢查,而只是渲染到 api 文檔上,來標記 api 將返回什么結(jié)果,以及應(yīng)該怎么調(diào)用 api。

然后介紹下目前用到的裝飾器:

@ns.doc 來標記這個 api 的作用

@ns.marshal_with 來標記如何渲染返回的 json

@ns.expect 來標記我們預期什么樣子的 request

運行程序我們可以看到以下結(jié)果:

我們也可以通過 try it 來調(diào)用 api:

查詢和更新

因為路由是綁定到一個類上的,因此限定了這個類能處理的 url,對于 "/users/user_id" 類似的路徑,需要多帶帶的類來處理:

@ns.route("/")
@ns.response(404, "User not found")
@ns.param("user_id", "The user identifier")
class UserInfoApi(Resource):
    users = [User("HanMeiMei"), User("LiLei")]
    print([u.user_id for u in users])

    @ns.doc("get_user_by_id")
    @ns.marshal_with(user_model)
    def get(self, user_id):
        for u in self.users:
            if u.user_id == user_id:
                return u
        ns.abort(404, "User {} doesn"t exist".format(user_id))

    @ns.doc("update_user_info")
    @ns.expect(user_model)
    @ns.marshal_with(user_model)
    def put(self, user_id):
        user = None
        for u in self.users:
            if u.user_id == user_id:
                user = u
        if not user:
            ns.abort(404, "User {} doesn"t exist".format(user_id))
        user.username = api.payload["username"]
        return user

在這里面可以看到更改了 url 和新引入了兩個裝飾器:

@ns.response 用來標記可能出現(xiàn)的 Response Status Code 并渲染在文檔中

@ns.param 用來標記 URL 參數(shù)

運行程序之后我們可以嘗試根據(jù) id 獲得一個用戶:

注意namespace 的 name 會被拼接到 url 中,比如上面 url 中的 “users” 即是 namespace name。

帶嵌套的 Api

用戶 Api 和圖書 Api 基本一樣而且簡單,但是對于訂單 Api 中,需要包含用戶信息和圖書信息,在實現(xiàn)上略微不同。

from flask_restplus import Resource, fields, Namespace

from model import Order, Book, User
from apis.user_api import user_model
from apis.book_api import book_model

ns = Namespace("order", description="Order CURD api.")

order_model = ns.model("OrderModel", {
    "order_id": fields.String(readOnly=True, description="The order unique identifier"),
    "user": fields.Nested(user_model, description="The order creator info"),
    "book": fields.Nested(book_model, description="The book info."),
    "created_at": fields.Integer(readOnly=True, description="create time: unix timestamp."),
})
order_list = ns.model("OrderListModel", {
    "orders": fields.List(fields.Nested(order_model)),
    "total": fields.Integer(description="len of orders")
})

book = Book("Book1", 10.5)
user = User("LiLei")
order = Order(user.user_id, book.book_id)


@ns.route("")
class UserListApi(Resource):

    @ns.doc("get_order_list")
    @ns.marshal_with(order_list)
    def get(self):
        return {
            "orders": [{
                "order_id": order.order_id,
                "created_at": order.created_at,
                "user": {
                    "user_id": user.user_id,
                    "username": user.username,
                },
                "book": {
                    "book_id": book.book_id,
                    "book_name": book.book_name,
                    "price": book.price,
                }
            }],
            "total": 1}

    @ns.doc("create_order")
    @ns.expect(order_model)
    @ns.marshal_with(order_model, code=201)
    def post(self):
        return {
            "order_id": order.order_id,
            "created_at": order.created_at,
            "user": {
                "user_id": user.user_id,
                "username": user.username,
            },
            "book": {
                "book_id": book.book_id,
                "book_name": book.book_name,
                "price": book.price,
            }
        }

這里使用了更靈活的格式組合,包括 fields.Nested 可以引入其他 model,因為 model 可以相互引用,因此還是有必要把這些 model 放在一起,來避免循環(huán)引用。不過由此也可以看出,Response 解析還是比較自由的。

備注:這里 return 的是一個字典,但是理想狀態(tài)下應(yīng)該是一個類(user 字段和 book 字段),只是因為沒有數(shù)據(jù)庫操作,簡化處理。

到這里,這個小項目就是寫完了,最后運行效果圖如下:

改造

可以通過這個簡單的 Demo 了解 Flask-RESTPlus 的使用,但是目前只是從零到一的寫一個完成的項目,因此看起來非常容易上手,但是如果是舊項目改造,我們需要做什么?

通過上述代碼,我們可以看到要做的主要是兩件事:

Api 層的改造

設(shè)計 Api Model

Api 層改造涉及到兩點,因為 url 是由 blueprint、api obj、namespace 三個東西共同組成的,因此需要設(shè)計怎么分配,可能還有重寫部分 api 的實現(xiàn)。但是理想的 api-service-model 架構(gòu)的程序, api 應(yīng)該是比較薄的一層,要接入并不困難,只是瑣碎。

Api Model 一般是原有項目沒有的,需要引入,其中包括的參數(shù)檢查的 model(Flask-RESTPlus 提供了 Request Parsing,本文并沒討論,可以參考文檔: Request Parsing )和解析 Response 的 model,這些需要梳理所有 api 和字段,工作量不小,如果數(shù)據(jù)庫模型設(shè)計合理的話也許能減輕部分工作量。

Swagger

Swagger 是一款非常流行的 Api 文檔管理、交互工具,適用于在團隊中的 Api 管理,以及服務(wù)組件對接。其好用與重要程度不必贅言,下面基于上文的 demo,完成一個 Swagger 文檔以及基于文檔生成用于對接的 client。

獲得 Swagger 文檔

Flask-RESTPlus 是已經(jīng)集成了 Swagger UI 的,在運行時所獲得界面即是通過 Swagger UI 渲染的。而我們目前需要的是獲取 Swagger 文檔 json 或 yaml 文件。

在控制臺可以看到,在訪問程序時:

是的,這就是 Swagger 文檔:

代碼生成

使用 Swagger 生成文檔需要

在 macOS 下載:

brew install swagger-codegen

然后可以通過 help 名稱查看幫助:

Hypo-MBP:~ hypo$ swagger-codegen help
usage: swagger-codegen-cli  []

The most commonly used swagger-codegen-cli commands are:
    config-help   Config help for chosen lang
    generate      Generate code with chosen lang
    help          Display help information
    langs         Shows available langs
    meta          MetaGenerator. Generator for creating a new template set and configuration for Codegen.  The output will be based on the language you specify, and includes default templates to include.
    validate      Validate specification
    version       Show version information

See "swagger-codegen-cli help " for more information on a specific
command.

生成 Python client:

swagger-codegen generate -i http://127.0.0.1:5000/api/swagger.json -l python

執(zhí)行完成后,便可以在當前路徑的 swagger_client 下找到 api client 了。

總結(jié)

本文介紹了 Flask-RESTPlus 的使用,因為其本身就支持 Swagger 語法并內(nèi)置了 Swagger UI,所以 Swagger 對接簡單異常。因此,主要工作量放在了編寫 api 層上,包括 model,以及 api 中起到解釋說明作用的裝飾器。雖然在代碼上需要編寫不少不必要的代碼(介紹說明用的描述等),但是這些額外代碼輔助生成了與代碼一致的文檔,在組件對接和維護上,實則降低了成本。

歡迎關(guān)注個人公眾號:CS實驗室

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/41699.html

相關(guān)文章

  • 使用swagger 生成 Flask RESTful API

    摘要:指定篩選條件選擇合適的狀態(tài)碼應(yīng)答中,需要帶一個很重要的字段。返回結(jié)果針對不同操作,服務(wù)器向用戶返回的結(jié)果應(yīng)該符合以下規(guī)范。如果狀態(tài)碼是,就應(yīng)該向用戶返回出錯信息。 什么是 RESTful 什么是REST REST(英文:Representational State Transfer,又稱具象狀態(tài)傳輸)是Roy Thomas Fielding博士于2000年在他的博士論文 中提出來的一種...

    printempw 評論0 收藏0
  • 【效率專精系列】善用API統(tǒng)一描述語言提升RestAPI開發(fā)效率

    摘要:其標準為前身是,提供強大的在線編輯功能,包括語法高亮錯誤提示自動完成實時預覽,并且支持用戶以格式撰寫導入導出轉(zhuǎn)換文檔。 團隊內(nèi)部RestAPI開發(fā)采用設(shè)計驅(qū)動開發(fā)的模式,即使用API設(shè)計文檔解耦前端和后端的開發(fā)過程,雙方只在聯(lián)調(diào)與測試時耦合。在實際開發(fā)和與前端合作的過程中,受限于眾多因素的影響,開發(fā)效率還有進一步提高的空間。本文的目的是優(yōu)化工具鏈支持,減少一部分重復和枯燥的勞動。 現(xiàn)狀...

    tianyu 評論0 收藏0
  • api 接口管理工具

    摘要:接口管理工具大致分為線上工具和自建工具。安裝其他工具上面講的,不管是線上工具還是自建工具,都是接口集成工具,主要是為了提供數(shù)據(jù)功能。類似網(wǎng)易云筆記印象筆記的筆記管理工具。 api 接口管理工具 現(xiàn)在,Web 應(yīng)用的前后端分離事實上已經(jīng)成為了大家都認可的一種開發(fā)方式,前后端分離之后,前端與后端都用接口(api)來溝通,這就需要我們做好 API 接口管理,所以,這次來聊聊 API 接口管理...

    marser 評論0 收藏0
  • api 接口管理工具

    摘要:接口管理工具大致分為線上工具和自建工具。安裝其他工具上面講的,不管是線上工具還是自建工具,都是接口集成工具,主要是為了提供數(shù)據(jù)功能。類似網(wǎng)易云筆記印象筆記的筆記管理工具。 api 接口管理工具 現(xiàn)在,Web 應(yīng)用的前后端分離事實上已經(jīng)成為了大家都認可的一種開發(fā)方式,前后端分離之后,前端與后端都用接口(api)來溝通,這就需要我們做好 API 接口管理,所以,這次來聊聊 API 接口管理...

    wuyumin 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<