すべてのFlaskルートにプレフィックスを追加します


98

すべてのルートに追加したいプレフィックスがあります。現在、すべての定義でルートに定数を追加しています。これを自動的に行う方法はありますか?

PREFIX = "/abc/123"

@app.route(PREFIX + "/")
def index_page():
  return "This is a website about burritos"

@app.route(PREFIX + "/about")
def about_page():
  return "This is a website about burritos"

回答:


75

答えは、このアプリケーションの提供方法によって異なります。

別のWSGIコンテナー内にサブマウント

このアプリケーションをWSGIコンテナー(mod_wsgi、uwsgi、gunicornなど)の内部で実行するとします。実際にマウントし、アプリケーションをそのWSGIコンテナーのサブパートとしてWSGIコンテナー(WSGIを話すものであれば何でもかまいません)にマウントし、構成APPLICATION_ROOT値をプレフィックスに設定する必要があります。

app.config["APPLICATION_ROOT"] = "/abc/123"

@app.route("/")
def index():
    return "The URL for this page is {}".format(url_for("index"))

# Will return "The URL for this page is /abc/123/"

設定APPLICATION_ROOT値を設定すると、FlaskのセッションCookieがそのURLプレフィックスに制限されます。それ以外のすべては、FlaskとWerkzeugの優れたWSGI処理機能によって自動的に処理されます。

アプリを適切にサブマウントする例

最初の段落の意味がわからない場合は、内部にFlaskがマウントされているこのサンプルアプリケーションをご覧ください。

from flask import Flask, url_for
from werkzeug.serving import run_simple
from werkzeug.wsgi import DispatcherMiddleware

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/abc/123'

@app.route('/')
def index():
    return 'The URL for this page is {}'.format(url_for('index'))

def simple(env, resp):
    resp(b'200 OK', [(b'Content-Type', b'text/plain')])
    return [b'Hello WSGI World']

app.wsgi_app = DispatcherMiddleware(simple, {'/abc/123': app.wsgi_app})

if __name__ == '__main__':
    app.run('localhost', 5000)

アプリへのリクエストのプロキシ

一方、WSGIコンテナーのルートでFlaskアプリケーションを実行してリクエストをプロキシしている場合(たとえば、FastCGIが実行されている場合、またはnginxがproxy_passサブエンドポイントのリクエストを実行している場合)スタンドアロンuwsgi/ geventサーバーに次のいずれかを行うことができます:

  • ミゲルが彼の答えで指摘しているように、青写真を使用します
  • または使用DispatcherMiddlewareからwerkzeug(またはPrefixMiddlewareからSU27の答えをあなたが使用しているスタンドアロンのWSGIサーバーにサブマウントアプリケーションに)。(使用するコードについては、上記のアプリを適切にサブマウントする例を参照してください)。

@jknupp-見てflask.Flask#create_url_adapterwerkzeug.routing.Map#bind_to_environそれうまくいくはずです-どのようにコードを実行していましたか?(実際には、アプリがWSGI環境のサブパスにマウントされていてurl_for、期待値を返す必要があります。)
Sean Vieira

私はあなたが書いたものを正確に実行しましたが、app = Flask(name)とapp.run(debug = True)を追加しました
jeffknupp

4
@jknupp-それが問題です-実際にアプリケーションをより大きなアプリケーションのサブパートとしてマウントする必要があります(WSGIを話すものなら何でもかまいません)。私は手早くきた例の要点を、私は、サブマウントWSGI環境だけでなく、サブパス要求を転送しているプロキシの背後にあるスタンドアローンのWSGI環境を仮定していること、それをより明確にするために私の答えを更新しました。
Sean Vieira

3
これは、DispatcherMiddlewareフラスコを単独で実行するときに、アプローチを使用して機能します。Gunicornの背後で実行しているときは、これがうまく機能していないようです。
ジャスティン

1
uwsgiのサブパスにマウントする方法uwsgi -s /tmp/yourapplication.sock --manage-script-name --mount /yourapplication=myapp:app。詳しくは、(uwsgi文書)を参照flask.pocoo.org/docs/1.0/deploying/uwsgi/]
todaynowork

94

あなたのルートを青写真に入れることができます:

bp = Blueprint('burritos', __name__,
                        template_folder='templates')

@bp.route("/")
def index_page():
  return "This is a website about burritos"

@bp.route("/about")
def about_page():
  return "This is a website about burritos"

次に、プレフィックスを使用してアプリケーションにブループリントを登録します。

app = Flask(__name__)
app.register_blueprint(bp, url_prefix='/abc/123')

2
こんにちはミゲル。以下で行ったようにブループリントのurl_prefixをapp.register_blueprint登録することと、上記のBlueprintオブジェクトをインスタンス化するときにそれを登録することの違いを知っていますかurl_prefix='/abc/123?ありがとうございました!
aralar 2014

4
違いは、register_blueprint呼び出しでURL接頭辞を使用すると、アプリケーションがブループリントを必要な場所に「マウント」したり、異なるURLに同じブループリントを複数回マウントしたりできるという点です。プレフィックスをブループリント自体に含めると、アプリケーションは簡単になりますが、柔軟性が低下します。
ミゲル

ありがとうございました!!それはとても役に立ちます。見かけ上の冗長性に混乱しましたが、2つのオプションのトレードオフがわかります。
aralar 2014

実際、私はこれを試したことはありませんが、ブループリントとアプリの両方でURLプレフィックスを組み合わせて、アプリのプレフィックスfistに続けてブループリントプレフィックスを付けることができる可能性があります。
ミゲル

4
blueprint.route装飾関数のでブループリントを登録する必要があることに注意してください。
Quint

53

APPLICATION_ROOTこれはこの目的のためではないことに注意してください。

次の変更を行うミドルウェアを作成するだけです。

  1. PATH_INFO接頭辞付きのURLを処理するように変更します。
  2. SCRIPT_NAME接頭辞付きのURLを生成するように変更します。

このような:

class PrefixMiddleware(object):

    def __init__(self, app, prefix=''):
        self.app = app
        self.prefix = prefix

    def __call__(self, environ, start_response):

        if environ['PATH_INFO'].startswith(self.prefix):
            environ['PATH_INFO'] = environ['PATH_INFO'][len(self.prefix):]
            environ['SCRIPT_NAME'] = self.prefix
            return self.app(environ, start_response)
        else:
            start_response('404', [('Content-Type', 'text/plain')])
            return ["This url does not belong to the app.".encode()]

次のように、アプリをミドルウェアでラップします。

from flask import Flask, url_for

app = Flask(__name__)
app.debug = True
app.wsgi_app = PrefixMiddleware(app.wsgi_app, prefix='/foo')


@app.route('/bar')
def bar():
    return "The URL for this page is {}".format(url_for('bar'))


if __name__ == '__main__':
    app.run('0.0.0.0', 9010)

訪問http://localhost:9010/foo/bar

あなたは正しい結果を得るでしょう: The URL for this page is /foo/bar

また、必要に応じてcookieドメインを設定することを忘れないでください。

この解決策はLarivactの要旨によって与えられます。APPLICATION_ROOTそれがためにのように見えるが、この仕事のためではありません。それは本当に混乱しています。


4
この回答を追加していただきありがとうございます。ここに掲載されている他の解決策を試してみましたが、これが私にとってうまくいった唯一の解決策です。A +++ Iはwfastcgi.pyを使用してIISにデプロイされています
sytech

「これAPPLICATION_ROOTはこの仕事のためではない」-これは私が間違っていたところです。私はBlueprinturl_prefixパラメーターを希望しAPPLICATION_ROOT、デフォルトで結合されているAPPLICATION_ROOTため、アプリ全体のurl_prefixスコープURL APPLICATION_ROOTと、個々のブループリントのスコープURLを含めることができます。ため息
モンクピット

私がを使おうとしていたことの例については、この要旨を参照してくださいAPPLICATION_ROOT
モンクピット2016年

2
gunicornを使用している場合、SCRIPT_NAMEはすでにサポートされています。環境変数として設定するか、httpヘッダーとして渡します:docs.gunicorn.org/en/stable/faq.html
blurrcat

1
現状のコードでは機能しませんでした。いくつかの調査の後、私は__call__メソッドresponse = Response('That url is not correct for this application', status=404) return response(environ, start_response)from werkzeug.wrappers import BaseResponse as Response
Louis Becker

10

これはFlask / werkzeugの回答よりもpythonの回答です。しかし、それはシンプルで機能します。

私のように、(.iniファイルからロードされた)アプリケーション設定にFlaskアプリケーションのプレフィックスも含めたい場合(つまり、デプロイメント時に値が設定されないように、実行時に設定されます)、以下を選択できます:

def prefix_route(route_function, prefix='', mask='{0}{1}'):
  '''
    Defines a new route function with a prefix.
    The mask argument is a `format string` formatted with, in that order:
      prefix, route
  '''
  def newroute(route, *args, **kwargs):
    '''New function to prefix the route'''
    return route_function(mask.format(prefix, route), *args, **kwargs)
  return newroute

おそらく、これはややハックであり、Flaskルート関数route最初の位置引数としてaを必要とするという事実に依存しています。

次のように使用できます。

app = Flask(__name__)
app.route = prefix_route(app.route, '/your_prefix')

注意:接頭辞で変数を使用して(たとえば、に設定することにより/<prefix>)、その後、で装飾する関数でこの接頭辞を処理できることは何の価値もありません@app.route(...)。その場合、明らかprefixに、装飾された関数でパラメーターを宣言する必要があります。さらに、送信されたプレフィックスをいくつかのルールに対してチェックし、チェックが失敗した場合は404を返すこともできます。404カスタムの再実装を避けるために、してくださいfrom werkzeug.exceptions import NotFound、その後、raise NotFound()チェックが失敗した場合。


を使用するよりもシンプルで効率的ですBlueprint。共有してくれてありがとう!
HKボーイ

5

したがって、これに対する有効な答えは、開発が完了したときに使用する実際のサーバーアプリケーションでプレフィックスを構成する必要があると思います。Apache、nginxなど

ただし、Flaskアプリをデバッグで実行しながら開発中にこれを機能させたい場合は、この要点を確認してください。

フラスコDispatcherMiddlewareは救助に!

後世のためにここにコードをコピーします。

"Serve a Flask app on a sub-url during localhost development."

from flask import Flask


APPLICATION_ROOT = '/spam'


app = Flask(__name__)
app.config.from_object(__name__)  # I think this adds APPLICATION_ROOT
                                  # to the config - I'm not exactly sure how!
# alternatively:
# app.config['APPLICATION_ROOT'] = APPLICATION_ROOT


@app.route('/')
def index():
    return 'Hello, world!'


if __name__ == '__main__':
    # Relevant documents:
    # http://werkzeug.pocoo.org/docs/middlewares/
    # http://flask.pocoo.org/docs/patterns/appdispatch/
    from werkzeug.serving import run_simple
    from werkzeug.wsgi import DispatcherMiddleware
    app.config['DEBUG'] = True
    # Load a dummy app at the root URL to give 404 errors.
    # Serve app at APPLICATION_ROOT for localhost development.
    application = DispatcherMiddleware(Flask('dummy_app'), {
        app.config['APPLICATION_ROOT']: app,
    })
    run_simple('localhost', 5000, application, use_reloader=True)

これで、上記のコードをスタンドアロンのFlaskアプリとして実行すると、http://localhost:5000/spam/が表示されますHello, world!

別の答えのコメントで、私は次のようなことをしたいと言いました:

from flask import Flask, Blueprint

# Let's pretend module_blueprint defines a route, '/record/<id>/'
from some_submodule.flask import module_blueprint

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/api'
app.register_blueprint(module_blueprint, url_prefix='/some_submodule')
app.run()

# I now would like to be able to get to my route via this url:
# http://host:8080/api/some_submodule/record/1/

DispatcherMiddleware私の不自然な例に適用すると:

from flask import Flask, Blueprint
from flask.serving import run_simple
from flask.wsgi import DispatcherMiddleware

# Let's pretend module_blueprint defines a route, '/record/<id>/'
from some_submodule.flask import module_blueprint

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/api'
app.register_blueprint(module_blueprint, url_prefix='/some_submodule')
application = DispatcherMiddleware(Flask('dummy_app'), {
    app.config['APPLICATION_ROOT']: app
})
run_simple('localhost', 5000, application, use_reloader=True)

# Now, this url works!
# http://host:8080/api/some_submodule/record/1/

「つまり、これに対する有効な答えは、開発が完了したときに使用する実際のサーバーアプリケーションでプレフィックスを構成する必要があると私は信じています。Apache、nginxなど」問題はリダイレクトにあります。プレフィックスがあり、Flaskで設定していない場合、/ yourprefix / path / to / urlに移動するのではなく、リダイレクトすると、/ path / to / urlに移動します。nginxまたはApacheで、接頭辞を設定する方法はありますか?
ジョーダンライター2016

おそらくこれを行う方法は、puppetやchefなどの構成管理ツールを使用し、そこに接頭辞を設定してから、ツールに変更を構成ファイルに反映させる必要があります。私がapacheまたはnginxについて話していることを知っているふりをするつもりはありません。この質問/回答はpythonに固有だったので、シナリオを別の質問として投稿することをお勧めします。これを行う場合は、こちらの質問に自由にリンクしてください。
Monkpit

2

別の完全に異なる方法は、のマウントポイントですuwsgi

同じプロセスで複数のアプリをホストすることに関するドキュメントから(パーマリンク)。

あなたuwsgi.iniに追加

[uwsgi]
mount = /foo=main.py
manage-script-name = true

# also stuff which is not relevant for this, but included for completeness sake:    
module = main
callable = app
socket = /tmp/uwsgi.sock

ファイルを呼び出さない場合main.pyは、mountとの両方を変更する必要がありますmodule

あなたmain.pyはこのように見えるかもしれません:

from flask import Flask, url_for
app = Flask(__name__)
@app.route('/bar')
def bar():
  return "The URL for this page is {}".format(url_for('bar'))
# end def

そして、nginx config(ここでも完全を期します):

server {
  listen 80;
  server_name example.com

  location /foo {
    include uwsgi_params;
    uwsgi_pass unix:///temp/uwsgi.sock;
  }
}

呼び出しexample.com/foo/barは自動的に適応するため、/foo/barフラスコのによって返されたように表示されurl_for('bar')ます。そうすることで、プレフィックスの問題なしにリンクが機能します。


2
from flask import Flask

app = Flask(__name__)

app.register_blueprint(bp, url_prefix='/abc/123')

if __name__ == "__main__":
    app.run(debug='True', port=4444)


bp = Blueprint('burritos', __name__,
                        template_folder='templates')

@bp.route('/')
def test():
    return "success"

1
説明の追加をご検討ください。
jpp

1
私が見つけた2つの素晴らしい説明は、exploreflask公式ドキュメントにありました
yuriploc

1

同様のいわゆる「コンテキストルート」が必要でした。私はWSGIScriptAliasを使用して/etc/httpd/conf.d/の下のconfファイルでそれを行いました:

myapp.conf:

<VirtualHost *:80>
    WSGIScriptAlias /myapp /home/<myid>/myapp/wsgi.py

    <Directory /home/<myid>/myapp>
        Order deny,allow
        Allow from all
    </Directory>

</VirtualHost>

だから今私は自分のアプリにアクセスすることができます:http:// localhost:5000 / myapp

ガイドを参照してください-http://modwsgi.readthedocs.io/en/develop/user-guides/quick-configuration-guide.html


1

フラスコとPHPアプリがnginxとPHP5.6が共存する私のソリューション

ルートにKEEP Flask、サブディレクトリにPHP

sudo vi /etc/php/5.6/fpm/php.ini

1行追加

cgi.fix_pathinfo=0
sudo vi /etc/php/5.6/fpm/pool.d/www.conf
listen = /run/php/php5.6-fpm.sock

uwsgi

sudo vi /etc/nginx/sites-available/default

PHPのネストされた場所を使用し、FLASKをルートに残す

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    # SSL configuration
    #
    # listen 443 ssl default_server;
    # listen [::]:443 ssl default_server;
    #
    # Note: You should disable gzip for SSL traffic.
    # See: https://bugs.debian.org/773332
    #
    # Read up on ssl_ciphers to ensure a secure configuration.
    # See: https://bugs.debian.org/765782
    #
    # Self signed certs generated by the ssl-cert package
    # Don't use them in a production server!
    #
    # include snippets/snakeoil.conf;

    root /var/www/html;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.php index.nginx-debian.html;

    server_name _;

    # Serve a static file (ex. favico) outside static dir.
    location = /favico.ico  {    
        root /var/www/html/favico.ico;    
    }

    # Proxying connections to application servers
    location / {
        include            uwsgi_params;
        uwsgi_pass         127.0.0.1:5000;
    }

    location /pcdp {
        location ~* \.php$ {
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }

    location /phpmyadmin {
        location ~* \.php$ {
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #   include snippets/fastcgi-php.conf;
    #
    #   # With php7.0-cgi alone:
    #   fastcgi_pass 127.0.0.1:9000;
    #   # With php7.0-fpm:
    #   fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #   deny all;
    #}
}

よく 読んでくださいhttps://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms

場所の一致を理解する必要があります(なし):修飾子が存在しない場合、場所はプレフィックスの一致として解釈されます。これは、指定された場所がリクエストURIの先頭と照合され、一致を判断することを意味します。=:等号が使用されている場合、リクエストURIが指定された場所と完全に一致すると、このブロックは一致と見なされます。〜:チルダ修飾子が存在する場合、この場所は大文字と小文字が区別される正規表現の一致として解釈されます。〜*:チルダとアスタリスクの修飾子が使用されている場合、ロケーションブロックは大文字と小文字を区別しない正規表現の一致として解釈されます。^〜:カラットとチルドの修飾子が存在し、このブロックが非正規表現の最適な一致として選択されている場合、正規表現の一致は行われません。

nginxの「場所」の説明から、順序は重要です。

特定のリクエストに一致する場所を見つけるために、nginxは最初にプレフィックス文字列を使用して定義された場所(プレフィックスの場所)をチェックします。その中で、一致するプレフィックスが最も長い場所が選択され、記憶されます。次に、構成ファイルでの出現順に正規表現がチェックされます。正規表現の検索は最初の一致で終了し、対応する構成が使用されます。正規表現との一致が見つからない場合は、前に記憶したプレフィックスの場所の構成が使用されます。

その意味は:

First =. ("longest matching prefix" match)
Then implicit ones. ("longest matching prefix" match)
Then regex. (first match)

1

まだこれで苦労している人にとって、最初の例は機能しますが、制御できないFlaskアプリがある場合、完全な例がここにあります。

from os import getenv
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from werkzeug.serving import run_simple
from custom_app import app

application = DispatcherMiddleware(
    app, {getenv("REBROW_BASEURL", "/rebrow"): app}
)

if __name__ == "__main__":
    run_simple(
        "0.0.0.0",
        int(getenv("REBROW_PORT", "5001")),
        application,
        use_debugger=False,
        threaded=True,
    )
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.