アプリケーションの振り分け(Application Dispatching) Application Dispatching

アプリケーションの振り分けは、WSGIレベルで複数のFlaskアプリケーションを併合するプロセスです。Flaskアプリケーションだけでなく、どんなWSGIアプリケーションでも併合できます。これは、もし望むならば、DjangoとFlaskのアプリケーションを同じインタープリタで横並びに走らせることを可能にします。このことの有益性は、内部的なアプリケーションの動き方に依存します。 Application dispatching is the process of combining multiple Flask applications on the WSGI level. You can combine not only Flask applications but any WSGI application. This would allow you to run a Django and a Flask application in the same interpreter side by side if you want. The usefulness of this depends on how the applications work internally.

パッケージにした大きなアプリケーション(Large Applications as Packages)との根本的な違いは、こちらのケースでは同一または異なるFlaskアプリケーションをお互いから全く分離して走らせていることです。それらは異なる設定で走り、WSGIのレベルで振り分けられます(dispatched)。 The fundamental difference from :doc:`packages` is that in this case you are running the same or different Flask applications that are entirely isolated from each other. They run different configurations and are dispatched on the WSGI level.

このドキュメントでの作業(Working with this Document) Working with this Document

以下に示す技法と例は、それぞれどんなWSGIサーバでも走らせることが可能なapplicationオブジェクトをもたらします。本番環境用には、展開の選択肢(Deployment Options)を確認してください。開発用には、Werkzeugはwerkzeug.serving.run_simple()で利用可能な開発用の組込みサーバを提供しています: Each of the techniques and examples below results in an ``application`` object that can be run with any WSGI server. For production, see :doc:`/deploying/index`. For development, Werkzeug provides a server through :func:`werkzeug.serving.run_simple`::

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

run_simpleは本番環境で使用するためにのものではないことに注意してください。(本番環境では)成熟したWSGIサーバ(production WSGI server)を使用してください。展開の選択肢(Deployment Options)を見てください。 Note that :func:`run_simple <werkzeug.serving.run_simple>` is not intended for use in production. Use a production WSGI server. See :doc:`/deploying/index`.

インタラクティブなデバッガを使用するためには、アプリケーションとsimple serverの両方でデバッグ機能(debugging)が有効になっている必要があります。以下は、デバッグ機能とrun_simpleを使った「hello world」の例です: In order to use the interactive debugger, debugging must be enabled both on the application and the simple server. Here is the "hello world" example with debugging and :func:`run_simple <werkzeug.serving.run_simple>`::

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)

アプリケーションの併合(Combining Applications) Combining Applications

もしも全く分かれたアプリケーションがあって、それらを同じPythonのインタープリタのプロセスで隣同士で動かしたい場合には、werkzeug.middleware.dispatcher.DispatcherMiddlewareを活用できます。そのときの考え方は、Flaskアプリケーションはそれぞれ正当なWSGIアプリケーションであり、それらはより大きなアプリケーションへdispatcher middlewareによって併合され、(URLのパスの)先頭部分(prefix)に基づいて振り分けられるというものです。 If you have entirely separated applications and you want them to work next to each other in the same Python interpreter process you can take advantage of the :class:`werkzeug.wsgi.DispatcherMiddleware`. The idea here is that each Flask application is a valid WSGI application and they are combined by the dispatcher middleware into a larger one that is dispatched based on prefix.

例えば、メインのアプリケーションを/で走らせ、バックエンドのインタフェースを/backendで走らせることができます: For example you could have your main application run on ``/`` and your backend interface on ``/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
})

サブドメインによる振り分け Dispatch by Subdomain

ときには、同じアプリケーションの複数のインスタンスを違う設定で使いたいかもしれません。アプリケーションが関数の内側で作られ、アプリケーションを初期化するためにその関数を呼び出せると仮定したとき、それを実装するのはとても容易です。関数の中での新しいインスタンスの作成をサポートするアプリケーションを開発するには、アプリケーション製造工場(Application Factories)パターンを確認してください。 Sometimes you might want to use multiple instances of the same application with different configurations. Assuming the application is created inside a function and you can call that function to instantiate it, that is really easy to implement. In order to develop your application to support creating new instances in functions have a look at the :doc:`appfactories` pattern.

とてもありがちな例は、アプリケーションをサブドメインごとに作成することでしょう。例えば、全てのサブドメインへの全てのリクエストを自分のアプリケーションへ振り分けるようにwebサーバを設定し、それからサブドメインの情報を使用してユーザ固有のインスタンスを作成します。全てのサブドメインを受信するように一度サーバを準備(set up)すれば、動的なアプリケーション作成をするとてもシンプルなWSGIアプリケーションを使用できます。 A very common example would be creating applications per subdomain. For instance you configure your webserver to dispatch all requests for all subdomains to your application and you then use the subdomain information to create user-specific instances. Once you have your server set up to listen on all subdomains you can use a very simple WSGI application to do the dynamic application creation.

この観点での抽象化の完璧なレベルは、WSGIの階層です。(以下の例では)受信するリクエストを調べ、そしてリクエストをFlaskアプリケーションへ移譲する、独自のWSGIアプリケーションを自分で作ります。もしもそのアプリケーションがまだ存在しない場合、それは動的に作成され記憶されます: The perfect level for abstraction in that regard is the WSGI layer. You write your own WSGI application that looks at the request that comes and delegates it to your Flask application. If that application does not exist yet, it is dynamically created and remembered::

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)

そして、この振り分け機能(dispatcher)は、以下のように使用できます: This dispatcher can then be used like this::

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)

パスによる振り分け(Dispatch by Path) Dispatch by Path

URLのパスによる振り分けは非常に似ています。サブドメインを理解するためにHostヘッダを調べる代わりに、シンプルにリクエストのパスを最初のスラッシュまで調べます: Dispatching by a path on the URL is very similar. Instead of looking at the ``Host`` header to figure out the subdomain one simply looks at the request path up to the first slash::

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を返したときは、別のアプリケーションで代わりに対応(fall back)することです: The big difference between this and the subdomain one is that this one falls back to another application if the creator function returns ``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)