10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

FlaskですべてのURLにPrefixを追加する

Posted at

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を継承して実装することにします。

custom_rule_app.py
# -*- 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. 動作確認

環境変数を設定した場合(APP_ROOT=/app/api/v1)
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には他にもたくさんの拡張ポイントが存在しますので、また別の機会に紹介していければと思います。

10
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?