2018年7月25日水曜日

flasgger で path を validation する方法

概要

flasgger でパラメータに path を使っている場合に validation する方法を紹介します
flasgger にはおそらくデフォルトでは path をチェックする機能は備わっていないので自分で実装する必要があります

環境

  • macOS X 10.13.6
  • Python 3.6.5
  • flasgger 0.9.0

Marshmallow Schemas を使って実装したベースアプリ

これをベースに validation 機能を付けてみます
path でパラメータを取るのでこれを validate してみます

  • vim app.py
# coding: utf-8
from flask import Flask, jsonify
from flasgger import Schema, Swagger, SwaggerView, fields


class CategorySchema(Schema):
    id = fields.Int()
    name = fields.Str(required=True)


class PetSchema(Schema):
    category = fields.Nested(CategorySchema, many=True)
    name = fields.Str()


class RandomView(SwaggerView):
    summary = 'A cute furry animal endpoint.'
    description = 'Get a random pet'
    parameters = [
        {
            'name': 'id',
            'in': 'path',
            'required': True,
            'type': 'integer'
        }
    ]
    responses = {
        200: {
            'description': 'A pet to be returned',
            'schema': PetSchema
        }
    }

    def get(self, id):
        pet = {'category': [{'id': id, 'name': 'rodent'}], 'name': 'Mickey'}
        return jsonify(PetSchema().dump(pet).data)


app = Flask(__name__)
app.add_url_rule(
    '/random/<id>',
    view_func=RandomView.as_view('random'),
    methods=['GET']
)

if __name__ == '__main__':
    app.run(debug=True)
  • pipenv run python3 app.py

で起動して

  • curl localhost:5000/random/1

で以下のようなレスポンスが返ってきます

{
  "category": [
    {
      "id": 1, 
      "name": "rodent"
    }
  ], 
  "name": "Mickey"
}

validation 機能を入れる前は文字列でも問題ありません
これが数字以外の場合はエラーになるようにします

validation 機能を追加する

まず import 系を少し追加します
validation 時にエラーのレスポンスを直接返却する必要があるので、それに関するモジュールやクラスを import します

import json
from werkzeug.exceptions import abort
from flask import Response

上記を追加しましょう
そして validation 用のメソッドを追加します

def my_validate(self, id):
    try:
        int(id)
    except ValueError as e:
        print(e)
        abort(
            Response(
                json.dumps({'error': 'id must be set integer type', 'id': id}),
                status=400
            )
        )

path で取得した id 情報を validation 用のメソッドに渡します
そして integer に変換できるか調査して、もし Exception が発生したらエラーを返却します
あとはこのメソッドをコールするだけです

def get(self, id):
    self.my_validate(id)
    pet = {'category': [{'id': id, 'name': 'rodent'}], 'name': 'Mickey'}
    return jsonify(PetSchema().dump(pet).data)

修正後の全体のコードは以下の通りです

# coding: utf-8
import json
from werkzeug.exceptions import abort
from flask import Response
from flask import Flask, jsonify
from flasgger import Schema, Swagger, SwaggerView, fields


class CategorySchema(Schema):
    id = fields.Int()
    name = fields.Str(required=True)


class PetSchema(Schema):
    category = fields.Nested(CategorySchema, many=True)
    name = fields.Str()


class RandomView(SwaggerView):
    summary = 'A cute furry animal endpoint.'
    description = 'Get a random pet'
    parameters = [
        {
            'name': 'id',
            'in': 'path',
            'required': True,
            'type': 'integer'
        }
    ]
    responses = {
        200: {
            'description': 'A pet to be returned',
            'schema': PetSchema
        }
    }

    def my_validate(self, id):
        try:
            int(id)
        except ValueError as e:
            print(e)
            abort(
                Response(
                    json.dumps({'error': 'id must be set integer type', 'id': id}),
                    status=400
                )
            )

    def get(self, id):
        self.my_validate(id)
        pet = {'category': [{'id': id, 'name': 'rodent'}], 'name': 'Mickey'}
        return jsonify(PetSchema().dump(pet).data)


app = Flask(__name__)
app.add_url_rule(
    '/random/<id>',
    view_func=RandomView.as_view('random'),
    methods=['GET']
)


if __name__ == '__main__':
    app.run(debug=True)

これで再度実行すると id の部分が文字列の場合にはエラーが返ってくるようになります

  • curl -v 'localhost:5000/random/a'
{"error": "id must be set integer type", "id": "a"}

validation = True と validation_function = None の機能について

実は flasgger には validation の機構が備わっています
validationvalidation_function という機能があります
validation はデフォルトで用意された validator で有効/無効の値しか設定できません
True にした場合に有効になります
やってくれることは例えば

  • HTTP メソッドチェック (405 チェック)
  • ContType のチェック、application/json かどうかのチェック
  • ボディが空でないかのチェック

などです
今回のようにフォーマットチェックなどしたい場合には正直使えません
True にしても余計なことをチェックするケースが多いかなと思います

また validation_function に関してですがこれは基本的に POST 時のボディを validate するときに使うっぽいです (?)
今回のように path のフォーマットチェックをする場合は素直に validation 用のメソッドを作ってしまうほうが早いです

最後に

flasgger の Marshmallow Schemas で path パラメータをチェックするための独自の validation 機能を実装してみました
おそらくこの方法が一番てっとり早いと思います
途中で軽く触れた validationvalidation_function の機能に関しても検証してみたいと思います

この辺りの情報はググっても出てこないので Github の issue やコードを見るしか方法がなさそうです

参考サイト

0 件のコメント:

コメントを投稿