国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Flask Api 文檔管理與 Swagger 上手

Scholer / 3563人閱讀

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

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

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

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

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

Flask-RESTPlus

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

安裝

安裝 Flask-RESTPlus

pip install flask-restplus

或者:

easy_install flask-restplus
最小 Demo

使用 Flask-RESTPlus 時需要按照這個庫規定的方式編寫 Api 層,包括 request 的參數解析,以及 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()

運行之后效果如下:

實踐

這里我會實現一個完整的小項目來實踐和介紹 Flask-RESTPlus 這個庫。我們實現一個簡單的 圖書訂單系統 ,實現用戶、圖書和訂單的 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 和創建時間:

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 中構建大型 Web 項目,可以通過藍圖為路由分組,并在藍圖中添加通用的規則(url 前綴、靜態文件路徑、模板路徑等)。這個項目我們只用一個 api 藍圖,在實際中可能會使用 openapi 藍圖,internal api 藍圖來區分大的分類。

Flask-RESTPlusclass::Api 將直接掛在在藍圖下面,這么我們即利用了 Flask 的藍圖進行對功能模塊分類,也可以利用 Api 的版本對 Api 版本進行管理,對于小的模塊分類,我們可以利用 Api 的 namespace,著這里我們可以分為 user namespacebook 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")

然后,就可以創建出不同的 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 的時候要避免循環引用,而且,這注冊藍圖的時候,需要先將 namespace 注冊,否則會 404。這個庫的很多方法太依賴 api 對象,感覺設計并不合理,很容易就循環引用,并不是非常優雅。

注冊 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 列表和創建

我們先完成用戶的列表和創建 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):
    # 初始化數據
    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

解釋下上面的代碼,首先需要創建一個 user model 來讓 Flask-RESTPlus 知道我們如何渲染和解析 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"),
})

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

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

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

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

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

運行程序我們可以看到以下結果:

我們也可以通過 try it 來調用 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 用來標記可能出現的 Response Status Code 并渲染在文檔中

@ns.param 用來標記 URL 參數

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

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

帶嵌套的 Api

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

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 放在一起,來避免循環引用。不過由此也可以看出,Response 解析還是比較自由的。

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

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

改造

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

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

Api 層的改造

設計 Api Model

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

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

Swagger

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

獲得 Swagger 文檔

Flask-RESTPlus 是已經集成了 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

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

總結

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

歡迎關注個人公眾號:CS實驗室

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41699.html

相關文章

  • 使用swagger 生成 Flask RESTful API

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

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

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

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

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

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

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

    wuyumin 評論0 收藏0

發表評論

0條評論

Scholer

|高級講師

TA的文章

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