GitHubじゃ!Pythonじゃ!

GitHubからPython関係の優良リポジトリを探したかったのじゃー、でも英語は出来ないから日本語で読むのじゃー、英語社会世知辛いのじゃー

rochacbruno

flasgger – Flask API用の簡単なOpenAPI仕様とSwagger UI

投稿日:

Flask API用の簡単なOpenAPI仕様とSwagger UI http://flasgger.pythonanywhere.com/

フラシュガー

あなたのFlask APIの簡単なSwagger UI

Flasggerは、APIに登録されているすべてのFlaskビューからOpenAPI = Specification抽出するFlask拡張機能です。

FlasggerにはSwaggerUIも組み込まれているので、http:// localhost:5000 / apidocsにアクセスして、APIリソースを視覚化して操作することができます。

Flasgger 、POST、PUT、PATCHとして受け取ったデータがYAMLPython辞書またはMarshmallow Schemasを使用して定義されたスキーマに対して有効であるかどうかを検証できる同じ仕様を使用して、着信データの検証も行います。

Flasggerは、docstringを仕様として使用する単純な関数ビューまたはMethodViews、または@swag_fromデコレータを使用してYAMLまたはdictから仕様を取得するSwaggerViewと、 Marshmallow Schemasを仕様として使用できるSwaggerViewを提供します。

FlasggerはFlask-RESTfulと互換性があり、 Resourcesswag仕様を一緒に使うことができるので、 安らかな例を見てください。

あなたがMarshmallowからAPISPecを使用している場合、Flasggerは仕様の基本テンプレートとしてMarshmallow APISpecをサポートしています。apispecの例を見てください。

サンプルとデモアプリ

いくつかのサンプルアプリケーションがあり、 Flasggerデモアプリでサンプルを再生することもできます

注記:すべてのサンプルアプリケーションはテストケースでもあり、品質とカバレッジを確保するためにTravis CIで自動的に実行されます。

インストール

あなたのvirtualenvの下で:

あなたが最新のsetuptoolsを持っていることを確認してください

pip install -U setuptools

次に

pip install flasgger

または(開発版)

pip install https://github.com/rochacbruno/flasgger/tarball/master

注: マシュマロスキーマを使用する場合は、 pip install marshmallow apispecを実行する必要があります。

入門

仕様としてのdocstringsの使用

たとえば、 colors.pyというファイルを作成します。

from flask import Flask, jsonify
from flasgger import Swagger

app = Flask(__name__)
swagger = Swagger(app)

@app.route('/colors/<palette>/')
def colors(palette):
    """Example endpoint returning a list of colors by palette
    This is using docstrings for specifications.
    ---
    parameters:
      - name: palette
        in: path
        type: string
        enum: ['all', 'rgb', 'cmyk']
        required: true
        default: all
    definitions:
      Palette:
        type: object
        properties:
          palette_name:
            type: array
            items:
              $ref: '#/definitions/Color'
      Color:
        type: string
    responses:
      200:
        description: A list of colors (may be filtered by palette)
        schema:
          $ref: '#/definitions/Palette'
        examples:
          rgb: ['red', 'green', 'blue']
    """
    all_colors = {
        'cmyk': ['cian', 'magenta', 'yellow', 'black'],
        'rgb': ['red', 'green', 'blue']
    }
    if palette == 'all':
        result = all_colors
    else:
        result = {palette: all_colors.get(palette)}

    return jsonify(result)

app.run(debug=True)

今すぐ実行する:

python colors.py

そして、 http:// localhost:5000 / apidocs /

あなたは:

外部YAMLファイルの使用

新しいファイルcolors.yml保存します

Example endpoint returning a list of colors by palette
In this example the specification is taken from external YAML file
---
parameters:
  - name: palette
    in: path
    type: string
    enum: ['all', 'rgb', 'cmyk']
    required: true
    default: all
definitions:
  Palette:
    type: object
    properties:
      palette_name:
        type: array
        items:
          $ref: '#/definitions/Color'
  Color:
    type: string
responses:
  200:
    description: A list of colors (may be filtered by palette)
    schema:
      $ref: '#/definitions/Palette'
    examples:
      rgb: ['red', 'green', 'blue']

ビュー関数だけを変更して同じ例を使用できます。

from flasgger import swag_from

@app.route('/colors/<palette>/')
@swag_from('colors.yml')
def colors(palette):
    ...

デコレータを使用したくない場合は、docstring file:ショートカットを使用できます。

@app.route('/colors/<palette>/')
def colors(palette):
    """
    file: colors.yml
    """
    ...

辞書を生の仕様として使用する

Python辞書を次のように作成します。

specs_dict = {
  "parameters": [
    {
      "name": "palette",
      "in": "path",
      "type": "string",
      "enum": [
        "all",
        "rgb",
        "cmyk"
      ],
      "required": "true",
      "default": "all"
    }
  ],
  "definitions": {
    "Palette": {
      "type": "object",
      "properties": {
        "palette_name": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/Color"
          }
        }
      }
    },
    "Color": {
      "type": "string"
    }
  },
  "responses": {
    "200": {
      "description": "A list of colors (may be filtered by palette)",
      "schema": {
        "$ref": "#/definitions/Palette"
      },
      "examples": {
        "rgb": [
          "red",
          "green",
          "blue"
        ]
      }
    }
  }
}

今度は同じ関数を使用し、YAMLファイルの代わりにdictを使用してください。

@app.route('/colors/<palette>/')
@swag_from(specs_dict)
def colors(palette):
    """Example endpoint returning a list of colors by palette
    In this example the specification is taken from specs_dict
    """
    ...

マシュマロスキーマの使用

最初に: pip install marshmallow apispec

from flask import Flask, jsonify
from flasgger import Swagger, SwaggerView, Schema, fields


class Color(Schema):
    name = fields.Str()

class Palette(Schema):
    pallete_name = fields.Str()
    colors = fields.Nested(Color, many=True)

class PaletteView(SwaggerView):
    parameters = [
        {
            "name": "palette",
            "in": "path",
            "type": "string",
            "enum": ["all", "rgb", "cmyk"],
            "required": True,
            "default": "all"
        }
    ]
    responses = {
        200: {
            "description": "A list of colors (may be filtered by palette)",
            "schema": Palette
        }
    }

    def get(self, palette):
        """
        Colors API using schema
        This example is using marshmallow schemas
        """
        all_colors = {
            'cmyk': ['cian', 'magenta', 'yellow', 'black'],
            'rgb': ['red', 'green', 'blue']
        }
        if palette == 'all':
            result = all_colors
        else:
            result = {palette: all_colors.get(palette)}
        return jsonify(result)

app = Flask(__name__)
swagger = Swagger(app)

app.add_url_rule(
    '/colors/<palette>',
    view_func=PaletteView.as_view('colors'),
    methods=['GET']
)

app.run(debug=True)

注:より完全な例については、 examples/validation.pyを見てください。

注:パスルールで引数をキャッチするときは常に明示的な型を使用してください。悪い場合は/api/<username> good: /api/<string:username>

Flask RESTfulリソースの使用

FlasggerはFlask-RESTfulと互換性があります。pipをインストールするだけで、 pip install flask-restfulから、

from flask import Flask
from flasgger import Swagger
from flask_restful import Api, Resource

app = Flask(__name__)
api = Api(app)
swagger = Swagger(app)

class Username(Resource):
    def get(self, username):
       """
       This examples uses FlaskRESTful Resource
       It works also with swag_from, schemas and spec_dict
       ---
       parameters:
         - in: path
           name: username
           type: string
           required: true
       responses:
         200:
           description: A single user item
           schema:
             id: User
             properties:
               username:
                 type: string
                 description: The name of the user
                 default: Steven Wilson
        """
        return {'username': username}, 200


api.add_resource(Username, '/username/<username>')

app.run(debug=True)

単一の関数の複数のhttpメソッドとルートの処理

エンドポイントまたは複数の方法で仕様を分けることができます

from flasgger.utils import swag_from

@app.route('/api/<string:username>', endpoint='with_user_name', methods=['PUT', 'GET'])
@app.route('/api/', endpoint='without_user_name')
@swag_from('path/to/external_file.yml', endpoint='with_user_name')
@swag_from('path/to/external_file_no_user_get.yml', endpoint='without_user_name', methods=['GET'])
@swag_from('path/to/external_file_no_user_put.yml', endpoint='without_user_name', methods=['PUT'])
def fromfile_decorated(username=None):
    if not username:
        return "No user!"
    return jsonify({'username': username})

また、 url_rule何度も登録することで、 MethodViewまたはSwaggerView複数のメソッドで同じことができます。 examples/example_app見てください

API POST本体の検証には同じデータを使用してください。

swag_from検証パラメータをTrue設定すると、受信データが自動的に検証されます。

from flasgger import swag_from

@swag_from('defs.yml', validation=True)
def post():
    # if not validate returns ValidationError response with status 400
    # also returns the validation message.

swagger.validateアノテーションを使用することもできます。

from flasgger import Swagger

swagger = Swagger(app)

@swagger.validate('UserSchema')
def post():
    '''
    file: defs.yml
    '''
    # if not validate returns ValidationError response with status 400
    # also returns the validation message.

しかし、あなたは手動でvalidateを呼び出すことができます:

from flasgger import swag_from, validate

@swag_from('defs.yml')
def post():
    validate(request.json, 'UserSchema', 'defs.yml')
    # if not validate returns ValidationError response with status 400
    # also returns the validation message.

また、 SwaggerView validation=Trueを定義し、 validation=TrueSwaggerViewを使用することもできます。

詳細についてはexamples/validation.pyを見てください。

すべての検証オプションはhttp://json-schema.org/latest/json-schema-validation.htmlにあります。

カスタム検証

デフォルトではFlasggerはpython-jsonschemaを使用して検証を行います。

カスタム検証関数は、要件を満たしていればサポートされています。

  • 2つのみ、位置的な引数を取る:
    • 最初に検証されるデータ。 そして
    • 2番目の引数として検証するスキーマ
  • 検証に失敗した場合は、あらゆる種類の例外を発生させます。

戻り値は破棄されます。

Swaggerインスタンスに関数を提供すると、デフォルトになります:

from flasgger import Swagger

swagger = Swagger(app, validation_function=my_validation_function)

swag_fromまたはswagger.validateアノテーションのパラメータとして、またはvalidate関数に直接関数を提供すると、Swaggerのデフォルトの検証関数よりも強制的に使用されます。

from flasgger import swag_from

@swag_from('spec.yml', validation=True, validation_function=my_function)
...
from flasgger import Swagger

swagger = Swagger(app)

@swagger.validate('Pet', validation_function=my_function)
...
from flasgger import validate

...

    validate(
        request.json, 'Pet', 'defs.yml', validation_function=my_function)

検証エラー処理

デフォルトでは、Flasggerは、エラーメッセージとともに400 BAD REQUEST応答で要求を中止することにより、検証エラーを処理します。

要件を満たしていれば、デフォルトの動作に代わるカスタム検証エラー処理機能を提供することができます。

  • 3つのみ、位置的な引数を取る:
    • 最初のエラーとしてエラーが発生しました。
    • 検証に失敗したデータは2番目のデータとなります。 そして
    • 3番目の引数として検証するために使用されたスキーマ

Swaggerインスタンスに関数を提供すると、デフォルトになります:

from flasgger import Swagger

swagger = Swagger(app, validation_error_handler=my_handler)

swag_fromまたはswagger.validateアノテーションのパラメータとして、またはvalidate関数に直接関数を提供すると、Swaggerのデフォルトの検証関数よりも強制的に使用されます。

from flasgger import swag_from

@swag_from(
    'spec.yml', validation=True, validation_error_handler=my_handler)
...
from flasgger import Swagger

swagger = Swagger(app)

@swagger.validate('Pet', validation_error_handler=my_handler)
...
from flasgger import validate

...

    validate(
        request.json, 'Pet', 'defs.yml',
        validation_error_handler=my_handler)

カスタム検証エラーハンドラ関数の使用例は、 例valid_error_handler.pyにあります。

Python辞書として定義されたスキーマを取得する

Swagger仕様で定義したスキーマを辞書として使用し、仕様を複製することなく使用することができます。 そのためには、 get_schemaメソッドを使用することができます:

from flask import Flask, jsonify
from flasgger import Swagger, swag_from

app = Flask(__name__)
swagger = Swagger(app)

@swagger.validate('Product')
def post():
    """
    post endpoint
    ---
    tags:
      - products
    parameters:
      - name: body
        in: body
        required: true
        schema:
          id: Product
          required:
            - name
          properties:
            name:
              type: string
              description: The product's name.
              default: "Guarana"
    responses:
      200:
        description: The product inserted in the database
        schema:
          $ref: '#/definitions/Product'
    """
    rv = db.insert(request.json)
    return jsonify(rv)

...

product_schema = swagger.get_schema('product')

このメソッドは、FlasggerスキーマID、すべての定義済みパラメータ、および必須パラメータのリストを含むディクショナリを返します。

HTMLサニタイザ

デフォルトではFlasggerはYAML定義の内容をすべて\n置き換えてサニタイズしようとしますが、この動作を変更して別の種類のサニタイザを設定することができます。

from flasgger import Swagger, NO_SANITIZER

app =Flask()
swagger = Swagger(app, sanitizer=NO_SANITIZER)

あなた自身のサニタイザーを書くことができます

swagger = Swagger(app, sanitizer=lambda text: do_anything_with(text))

MK_SANITIZERを使用して仕様記述にMarkdownをレンダリングできるようにするには、Markdownパーサーも利用できます

スウィガーUIとテンプレート

アプリケーションのtemplates/flasgger/index.htmlを上書きすることができ、このテンプレートはSwaggerUIのindex.htmlになります。 カスタマイズの基礎としてflasgger/ui2/templates/index.htmlを使用してください。

FlasggerはSwagger UIバージョン2と3をサポートしています。バージョン3はまだ実験中ですが、 app.config['SWAGGER']['uiversion']設定してみてください。

app = Flask(__name__)
app.config['SWAGGER'] = {
    'title': 'My API',
    'uiversion': 3
}
swagger = Swagger(app)

Flasggerをデフォルトデータで初期化する。

テンプレートを提供するデフォルトのデータでSwagger仕様を開始することができます:

template = {
  "swagger": "2.0",
  "info": {
    "title": "My API",
    "description": "API for my data",
    "contact": {
      "responsibleOrganization": "ME",
      "responsibleDeveloper": "Me",
      "email": "me@me.com",
      "url": "www.me.com",
    },
    "termsOfService": "http://me.com/terms",
    "version": "0.0.1"
  },
  "host": "mysite.com",  # overrides localhost:500
  "basePath": "/api",  # base bash for blueprint registration
  "schemes": [
    "http",
    "https"
  ],
  "operationId": "getmyData"
}

swagger = Swagger(app, template=template)

そして、テンプレートは、一部のビューで変更されない限り、デフォルトのデータです。 すべての仕様をテンプレートとして提供し、ビューを持たないこともできます。 または外部APPのビュー。

実行時にデフォルトデータを取得する

動的な値に応じて実行時にいくつかのデータを取得する必要がある場合がありhttpsLazyStringrequest.is_secureをチェックして、 LazyStringを使用してschemeshttpsなるかどうかを判断しLazyString

from flask import Flask
from flasgger import, Swagger, LazyString, LazyJSONEncoder

app = Flask(__init__)

# Set the custom Encoder (Inherit it if you need to customize)
app.json_encoder = LazyJSONEncoder


template = dict(
    info={
        'title': LazyString(lambda: 'Lazy Title'),
        'version': LazyString(lambda: '99.9.9'),
        'description': LazyString(lambda: 'Hello Lazy World'),
        'termsOfService': LazyString(lambda: '/there_is_no_tos')
    },
    host=LazyString(lambda: request.host),
    schemes=[LazyString(lambda: 'https' if request.is_secure else 'http')],
    foo=LazyString(lambda: "Bar")
)
Swagger(app, template=template)

LazyString値は、 jsonify実行時に値をエンコードする場合にのみ評価されるため、Flask request, session, g, etc..にアクセスでき、データベースにアクセスすることもできます。

リバースプロキシの背後

場合によっては、リバースプロキシ(NGINXなど)の背後にある傲慢なドキュメントを提供していることがあります。 Flaskのガイダンスに従えば、スワッガーのドキュメントは正しく読み込まれますが、 “試してください”ボタンが間違った場所を指しています。 これは次のコードで修正できます:

from flask import Flask, request
from flask import Swagger, LazyString, LazyJSONEncoder

app = Flask(__name__)
app.json_encoder = LazyJSONEncoder

template = dict(swaggerUiPrefix=LazyString(lambda : request.environ.get('HTTP_X_SCRIPT_NAME', '')))
swagger = Swagger(app, template=template)

デフォルト設定をカスタマイズする

異なる仕様ルートやSwagger UIを無効にするなどのカスタム設定をFlasggerに提供することができます。

swagger_config = {
    "headers": [
    ],
    "specs": [
        {
            "endpoint": 'apispec_1',
            "route": '/apispec_1.json',
            "rule_filter": lambda rule: True,  # all in
            "model_filter": lambda tag: True,  # all in
        }
    ],
    "static_url_path": "/flasgger_static",
    # "static_folder": "static",  # must be set by user
    "swagger_ui": True,
    "specs_route": "/apidocs/"
}

swagger = Swagger(app, config=swagger_config)

定義の抽出

idがspecで見つかると、定義を抽出できます。例:

from flask import Flask, jsonify
from flasgger import Swagger

app = Flask(__name__)
swagger = Swagger(app)

@app.route('/colors/<palette>/')
def colors(palette):
    """Example endpoint returning a list of colors by palette
    ---
    parameters:
      - name: palette
        in: path
        type: string
        enum: ['all', 'rgb', 'cmyk']
        required: true
        default: all
    responses:
      200:
        description: A list of colors (may be filtered by palette)
        schema:
          id: Palette
          type: object
          properties:
            palette_name:
              type: array
              items:
                schema:
                  id: Color
                  type: string
        examples:
          rgb: ['red', 'green', 'blue']
    """
    all_colors = {
        'cmyk': ['cian', 'magenta', 'yellow', 'black'],
        'rgb': ['red', 'green', 'blue']
    }
    if palette == 'all':
        result = all_colors
    else:
        result = {palette: all_colors.get(palette)}

    return jsonify(result)

app.run(debug=True)

この例では、 definitionsを渡す必要はありませんが、スキーマにidを追加する必要があります。







-rochacbruno
-, , , , , , , , , , , ,

執筆者: