1. はじめに
Flaskの拡張ポイントを利用して、JavaEEのWebアプリのコンテキスト名のように、全てのURLにPrefixを追加する方法について説明します。実装サンプルとしてはPrefixを環境変数(APP_ROOT
)で設定できるようにしたいと思います。
1.1. 検証環境
- Python 2.7.13
- Flask 0.12.2
2. URLのマッピングルールはurl_rule_class
で変更
FlaskでURLのマッピングルールの処理を行うクラスはurl_rule_class
で変更することができます。
デフォルトの設定はwerkzeug.routing.Rule
クラスです。
Flaskの公式ドキュメントにもしっかりと書かれています。
http://flask.pocoo.org/docs/0.12/api/#flask.Flask.url_rule_class
url_rule_class
The rule object to use for URL rules created. This is used by
add_url_rule(). Defaults to werkzeug.routing.Rule.New in version 0.7.
2.1. ソースコード
環境変数(APP_ROOT
)に設定されているPrefixを全てのURLに付与するRuleクラスを作成し、url_rule_class
で変更するようにします。
なお、Ruleクラスをゼロから作成するのは大変なので、今回はwerkzeug.routing.Rule
を継承して実装することにします。
# -*- coding: utf-8 -*-
import os
from flask import Flask, make_response, jsonify
from werkzeug.routing import Rule
# flask
app = Flask(__name__)
# get prefix from environment variable
APP_ROOT = os.getenv("APP_ROOT")
if not APP_ROOT is None:
# define custom_rule class
class Custom_Rule(Rule):
def __init__(self, string, *args, **kwargs):
# check endswith '/'
if APP_ROOT.endswith('/'):
prefix_without_end_slash = APP_ROOT.rstrip('/')
else:
prefix_without_end_slash = APP_ROOT
# check startswith '/'
if APP_ROOT.startswith('/'):
prefix = prefix_without_end_slash
else:
prefix = '/' + prefix_without_end_slash
super(Custom_Rule, self).__init__(prefix + string, *args, **kwargs)
# set url_rule_class
app.url_rule_class = Custom_Rule
# rest api
@app.route('/hello', methods=['GET'])
def hello_world():
return make_response(jsonify({'result':'hello world!'}))
@app.route('/goodbye', methods=['GET'])
def goodbye():
return make_response(jsonify({'result':'goodbye!'}))
# main
if __name__ == "__main__":
# 利用しているurl_rule_classを表示
print app.url_rule_class
# Flaskのマッピング情報を表示
print app.url_map
app.run(host='localhost', port=3000)
2.2. 動作確認
C:\demo>set APP_ROOT=/app/api/v1
C:\demo>python custom_rule_app.py
<class '__main__.Custom_Rule'>
Map([<Custom_Rule '/app/api/v1/goodbye' (HEAD, OPTIONS, GET) -> goodbye>,
<Custom_Rule '/app/api/v1/hello' (HEAD, OPTIONS, GET) -> hello_world>,
<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>])
* Running on http://localhost:3000/ (Press CTRL+C to quit)
127.0.0.1 - - [02/Oct/2017 16:27:42] "GET /app/api/v1/hello HTTP/1.1" 200 -
127.0.0.1 - - [02/Oct/2017 16:28:00] "GET /app/api/v1/goodbye HTTP/1.1" 200 -
127.0.0.1 - - [02/Oct/2017 16:28:12] "GET /hello HTTP/1.1" 404 -
127.0.0.1 - - [02/Oct/2017 16:28:18] "GET /goodbye HTTP/1.1" 404 -
環境変数(APP_ROOT
)に/app/api/v1
を設定してデモアプリを起動します。
標準出力の<class '__main__.Custom_Rule'>
から、今回作成したRuleクラスが適用されているのが分かるかと思います。また、マッピング情報を見るとPrefixとして/app/api/v1
がしっかりと付与されています。
実際にGETで/app/api/v1/hello
にアクセスすると正常に動作し、ソースコードの@app.route('/hello', methods=['GET'])
に該当する/hello
にアクセスすると404エラーになります。
マッピング情報をよく見ると<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>])
となっています。つまり、静的ファイルの処理にはPrefixが付与されません。これは公式ドキュメントの「This is used by add_url_rule().」が答えです。静的ファイルのマッピングには add_url_rule() が使われないためです。
静的ファイルのURLを変更する方法については「Flaskで静的ファイルの格納ディレクトリとURLを変更する」に記載していますので、こちらを参照ください。
C:\demo>set APP_ROOT
環境変数 APP_ROOT が定義されていません
C:\demo>python custom_rule_app.py
<class 'werkzeug.routing.Rule'>
Map([<Rule '/goodbye' (HEAD, OPTIONS, GET) -> goodbye>,
<Rule '/hello' (HEAD, OPTIONS, GET) -> hello_world>,
<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>])
* Running on http://localhost:3000/ (Press CTRL+C to quit)
環境変数(APP_ROOT
)が設定されていない場合、デフォルトのルールクラスであるwerkzeug.routing.Rule
のままになっているのが分かるかと思います。
3. さいごに
一般に公開されているマイクロサービス(REST API)ではURLに/api/v1
等が含まれているものが多いかと思います。
今回はこのようなお決まり的な値をAP開発者全員が意識することなくコーディングできるよう、Flaskの拡張ポイントであるurl_rule_class
を利用して、全てのURLにPrefixを追加する方法について説明しました。
Flaskには他にもたくさんの拡張ポイントが存在しますので、また別の機会に紹介していければと思います。