技術

Nginx+Flask+MySQLでREST APIを構築【4】

docker-composeで、Nginx+Flask+MySQLの構成でREST APIを実装していきます。

前回で、DB周りの設定が終わり、あとはビジネスロジックを作り込んでいくだけのフェーズなので、もう特に書くことはないと思っていました。

Nginx+Flask+MySQLでREST APIを構築【3】docker-composeで、Nginx+Flask+MySQLの構成でREST APIを実装していきます。 前回は、とりあえずサン...

が、細々と設定を追加することがあったので、補足していきたいと思います。

ディレクトリ構成(ファイル構成)

略 (前回参照)

API Keyの設定

今回構築したAPIはパブリックのものではないけど、パブリックアクセス(インターネットアクセス)が必要という要件でした。

使用者は限られるので、User認証は必要ないけどフルオープンもよろしくない。ということで、API Keyでセキュリティをかけることにしました。というわけで、作成してあるヘルスチェック用のAPIにkeyを設定していきます。

といっても、ググって出てきたものをほぼ丸々つかっただけですが。

やることはヘッダーの”X-Api-Key”に格納されているkeyが、API側で定義しているものと一致しているかチェックする関数をつくって、それを認証をかけたいControllerにデコレートするだけです。

まずはAPIのkeyチェックの関数を作ります。

security.pyという名前で、app.pyやdb.pyと同じ階層につくりました。

security.py

import functools

from flask import jsonify
from flask import request

import config


def api_required(func):
    @functools.wraps(func)
    def decorator(*args, **kwargs):
        headers = request.headers
        auth = headers.get("X-Api-Key")
        if auth == "password":
            return func(*args, **kwargs)
        else:
            return jsonify({"message": "ERROR: Unauthorized"}), 401

    return decorator

これをヘルチェックのControllerでデコレータとして使用します。

controller/health.py

from flask import Blueprint

from security import api_required

bp = Blueprint("health", __name__, url_prefix="/health")


@bp.route("/", methods=["GET"])
@api_required
def get():
    return "OK"

以上で完了です。

リクエストヘッダーに、”password”という値の”X-Api-Key”が入っているときだけ、レスポンスを返すようになります。

認証パスワードは、とりあえずハードコーディングにしました。シークレット化はリリース時に考えることにします。

ロガーの設定

今は、ログは後で集計とかしやすいようにJSON形成で吐くのが一般的です。

なので、JSON形式でログ出力するように設定していきます。Pythonやflaskの標準機能だけでやるのはきついので、python-json-loggerというライブラリを使います。

docker/api/requirement.txt に以下を追記します。

python-json-logger==2.0.2

続いてloggerの設定をしていきます。app.pyやdb.pyと同じ階層に、logger.pyを用意しました。

logger.py

import logging

from pythonjsonlogger import jsonlogger

logging.basicConfig(level=logging.INFO)

handler = logging.StreamHandler()
formatter = jsonlogger.JsonFormatter('%(asctime)s %(name)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
logging.getLogger().addHandler(handler)

app.pyでlogger.pyをimportします。app.py自体では使わなくても、ここに記述しないとログを出力してくれません。

app.py

import logger

このログやらモデルやら、使わなくてもapp.pyに記述していかないといけないのはflaskの使用なのでしょうか? 何かもっと言いやり方もある気がしますが、私にはわかりませんでした。

このloggerを使いたい場所で呼び出せば、JSON形式でログが出力されるようになります。

以下はControllerで使った場合。

import logging

from flask import Blueprint

from security import api_required

bp = Blueprint("health", __name__, url_prefix="/health")
logger = logging.getLogger("Controller:health")


@bp.route("/", methods=["GET"])
@api_required
def get():
    logger.info("/heals is called.")
    return "OK"

まとめ

以上、flaskでREST API構築したとき、API Key設定とロガーの設定についてでした。