Flask Apiドキュメント管理とSwagger


この記事はブログで最初に発表されました.https://blog.ihypo.net/152551...
Flaskは自由度が高く、柔軟性が高いことで知られるPython Webフレームワークです.しかし、高い柔軟性は、コードのメンテナンスコストが無限であることを意味し、高い自由度は、コードの品質がプログラマー自身に依存し、一貫した基準と規範がないことを意味します.そのため、チーム内で開発する場合、Flaskプロジェクトはコードとドキュメント仕様を確立して、大きなばらつきがないことを保証する必要があります.
本稿では,FlaskプロジェクトのApi仕様をApiの観点から探究し,Apiドキュメントの最適姿勢を得る.ドキュメントの作成と整理には、コードの作成に劣らず大きな労力がかかることはよく知られています.そのため、時間がきつい場合、ドキュメントはまず無視される作業です.しかし、プロジェクトが初期にドキュメントが存在しても、後続の反復では、ドキュメントの遅れが深刻であり、ドキュメントがないよりも誤導が発生します.
したがって、個人的には、ドキュメントはコードに従って移動し、コードが変更されたときにドキュメントもフォローアップしなければならないと考えていますが、人間は信頼できない原則に基づいて、ドキュメントは人工的にメンテナンスするのではなく、コードによって生成されるのが理想的です.コードに変更があれば、ドキュメントも自動的に更新され、非常に優雅なことになります.多くのドキュメントでは現実的ではありませんが、Apiドキュメントでは実現コストは高くありません.
Flask-RESTPlus
REST Apiにとって、Flask-RESTPlus優れたApiドキュメント生成ツールであり、このパッケージはFlaskルーティング層の作成方式を置き換え、自分の文法でApiの詳細を規定し、Apiドキュメントを生成する.
インストール
インストールFlask-RESTPlus:
pip install flask-restplus

または、
easy_install flask-restplus

最小DemoFlask-RESTPlusを使用する場合は、requestのパラメータ解析やresponseの戻りフォーマットなど、このライブラリで規定された方法でApiレイヤを記述する必要があります.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
idとusernameを含むユーザーmodel:
class User(object):
    user_id = None
    username = None

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

図書モデル、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

注文モデル、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青写真を1つしか使いませんが、実際にはopenapi青写真、internal api青写真を使って大きな分類を区別する可能性があります.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")

そして、異なる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-RESTPlusjsonのレンダリングと解析方法を知る必要があります.
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を呼び出すこともできます.
    クエリーと更新
    ルーティングはクラスにバインドされるため、'/users/user_について、このクラスが処理できるurlが限定される.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の変更と新しく導入された2つの装飾器が表示されます.
  • @ns.response表示可能なResponse Status Codeをマークしてドキュメントにレンダリングする
  • @ns.paramURLパラメータのタグ付け用
  • プログラムを実行した後、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他のモデルを導入できるなど、より柔軟なフォーマットの組み合わせを使用しています.モデルは互いに参照できるので、これらのモデルを一緒に置いて、循環参照を避ける必要があります.しかし,Response解析は比較的自由であることも分かる.
    メモ:ここでreturnは辞書ですが、理想的にはクラス(userフィールドとbookフィールド)であるべきです.データベース操作がないため、処理を簡素化します.
    ここまで、この小さなプロジェクトは書き終わったので、最後の運行効果図は以下の通りです.
    改造する
    この簡単なデモでわかるFlask-RESTPlusの使用ですが、現在はゼロから1までの完成した項目を書いているだけなので、とても使いやすいように見えますが、古い項目の改造であれば、私たちは何をする必要がありますか?
    上記のコードを使用すると、主に2つのことがわかります.
  • Api層の改造
  • デザインApi Model
  • Api層の改造は2点に及ぶ.urlはblueprint、api obj、namespaceの3つのものから構成されているため、どのように分配するかを設計する必要があり、apiの一部を書き換える実現もある可能性がある.しかし、理想的なapi-service-modelアーキテクチャのプログラムは、apiが比較的薄い層であるべきで、アクセスするのは難しくなく、些細なことです.
    Api Modelは一般的に既存のプロジェクトにはないもので、パラメータチェックのモデル(Flask-RESTPlusRequest Parsingが提供されているが、本論文では議論されていないが、ドキュメント:Request Parsing)とResponseを解析するモデルを参照することができる.これらは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クライアントの生成:
    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ドッキングは単純で異常である.従って,modelを含むapi層の記述に主な作業量を置き,apiにおいて説明の役割を果たす装飾器を含む.コードには不要なコード(説明用の説明など)を多く記述する必要がありますが、これらの追加コードはコードと一致するドキュメントを生成することを支援し、コンポーネントのドッキングとメンテナンスではコストを削減します.
    個人公衆番号へようこそ:CS実験室