アプリケーションのディスパッチ—フラスコのドキュメント

提供:Dev Guides
< FlaskFlask/docs/1.0.x/patterns/appdispatch
移動先:案内検索

アプリケーションのディスパッチ

アプリケーションディスパッチは、WSGIレベルで複数のFlaskアプリケーションを組み合わせるプロセスです。 Flaskアプリケーションだけでなく、任意のWSGIアプリケーションを組み合わせることができます。 これにより、必要に応じて、同じインタープリターでDjangoアプリケーションとFlaskアプリケーションを並べて実行できます。 これの有用性は、アプリケーションが内部でどのように機能するかによって異なります。

モジュールアプローチとの根本的な違いは、この場合、互いに完全に分離された同じまたは異なるFlaskアプリケーションを実行していることです。 それらは異なる構成を実行し、WSGIレベルでディスパッチされます。

このドキュメントの操作

以下の各手法と例により、任意のWSGIサーバーで実行できるapplicationオブジェクトが作成されます。 実稼働については、展開オプションを参照してください。 開発のために、Werkzeugはwerkzeug.serving.run_simple()で利用可能な開発用の組み込みサーバーを提供します。

from werkzeug.serving import run_simple
run_simple('localhost', 5000, application, use_reloader=True)

run_simpleは本番環境での使用を目的としていないことに注意してください。 本格的なWSGIサーバーを使用してください。

対話型デバッガーを使用するには、アプリケーションと単純サーバーの両方でデバッグを有効にする必要があります。 デバッグとrun_simpleを使用した「helloworld」の例を次に示します。

from flask import Flask
from werkzeug.serving import run_simple

app = Flask(__name__)
app.debug = True

@app.route('/')
def hello_world():
    return 'Hello World!'

if __name__ == '__main__':
    run_simple('localhost', 5000, app,
               use_reloader=True, use_debugger=True, use_evalex=True)

アプリケーションの組み合わせ

完全に分離されたアプリケーションがあり、それらを同じPythonインタープリタープロセスで互いに隣接して動作させたい場合は、werkzeug.wsgi.DispatcherMiddlewareを利用できます。 ここでの考え方は、各Flaskアプリケーションが有効なWSGIアプリケーションであり、ディスパッチャーミドルウェアによって結合されて、プレフィックスに基づいてディスパッチされるより大きなアプリケーションになるということです。

たとえば、メインアプリケーションを/で実行し、バックエンドインターフェイスを/backendで実行することができます。

from werkzeug.middleware.dispatcher import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend

application = DispatcherMiddleware(frontend, {
    '/backend': backend
})

サブドメインによる発送

同じアプリケーションの複数のインスタンスを異なる構成で使用したい場合があります。 アプリケーションが関数内で作成され、その関数を呼び出してインスタンス化できると仮定すると、実装は非常に簡単です。 関数での新しいインスタンスの作成をサポートするアプリケーションを開発するには、アプリケーションファクトリパターンを確認してください。

非常に一般的な例は、サブドメインごとにアプリケーションを作成することです。 たとえば、すべてのサブドメインに対するすべてのリクエストをアプリケーションにディスパッチするようにWebサーバーを構成してから、サブドメイン情報を使用してユーザー固有のインスタンスを作成します。 すべてのサブドメインをリッスンするようにサーバーを設定したら、非常に単純なWSGIアプリケーションを使用して動的なアプリケーションを作成できます。

その点での抽象化に最適なレベルは、WSGIレイヤーです。 来るリクエストを見て、それをFlaskアプリケーションに委任する独自のWSGIアプリケーションを作成します。 そのアプリケーションがまだ存在しない場合は、動的に作成され、記憶されます。

from threading import Lock

class SubdomainDispatcher(object):

    def __init__(self, domain, create_app):
        self.domain = domain
        self.create_app = create_app
        self.lock = Lock()
        self.instances = {}

    def get_application(self, host):
        host = host.split(':')[0]
        assert host.endswith(self.domain), 'Configuration error'
        subdomain = host[:-len(self.domain)].rstrip('.')
        with self.lock:
            app = self.instances.get(subdomain)
            if app is None:
                app = self.create_app(subdomain)
                self.instances[subdomain] = app
            return app

    def __call__(self, environ, start_response):
        app = self.get_application(environ['HTTP_HOST'])
        return app(environ, start_response)

このディスパッチャは、次のように使用できます。

from myapplication import create_app, get_user_for_subdomain
from werkzeug.exceptions import NotFound

def make_app(subdomain):
    user = get_user_for_subdomain(subdomain)
    if user is None:
        # if there is no user for that subdomain we still have
        # to return a WSGI application that handles that request.
        # We can then just return the NotFound() exception as
        # application which will render a default 404 page.
        # You might also redirect the user to the main page then
        return NotFound()

    # otherwise create the application for the specific user
    return create_app(user)

application = SubdomainDispatcher('example.com', make_app)

パスによるディスパッチ

URLのパスによるディスパッチは非常に似ています。 Hostヘッダーを調べてサブドメインを特定する代わりに、最初のスラッシュまでの要求パスを調べるだけです。

from threading import Lock
from werkzeug.wsgi import pop_path_info, peek_path_info

class PathDispatcher(object):

    def __init__(self, default_app, create_app):
        self.default_app = default_app
        self.create_app = create_app
        self.lock = Lock()
        self.instances = {}

    def get_application(self, prefix):
        with self.lock:
            app = self.instances.get(prefix)
            if app is None:
                app = self.create_app(prefix)
                if app is not None:
                    self.instances[prefix] = app
            return app

    def __call__(self, environ, start_response):
        app = self.get_application(peek_path_info(environ))
        if app is not None:
            pop_path_info(environ)
        else:
            app = self.default_app
        return app(environ, start_response)

これとサブドメインの大きな違いは、作成者関数がNoneを返す場合、これは別のアプリケーションにフォールバックすることです。

from myapplication import create_app, default_app, get_user_for_prefix

def make_app(prefix):
    user = get_user_for_prefix(prefix)
    if user is not None:
        return create_app(user)

application = PathDispatcher(default_app, make_app)