應用程式分派

應用程式分派是在 WSGI 層級組合多個 Flask 應用程式的過程。您不僅可以組合 Flask 應用程式,還可以組合任何 WSGI 應用程式。如果您願意,這將允許您在同一個直譯器中並排執行 Django 和 Flask 應用程式。這項功能的實用性取決於應用程式內部的運作方式。

大型應用程式作為套件的根本區別在於,在這種情況下,您正在執行相同或不同的 Flask 應用程式,它們彼此完全隔離。它們運行不同的配置,並在 WSGI 層級進行分派。

使用本文檔

以下每種技術和範例都會產生一個 application 物件,該物件可以使用任何 WSGI 伺服器執行。對於開發,請使用 flask run 命令來啟動開發伺服器。對於生產環境,請參閱部署到生產環境

from flask import Flask

app = Flask(__name__)

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

組合應用程式

如果您有完全分離的應用程式,並且希望它們在同一個 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
})

通過子網域分派

有時您可能希望使用同一個應用程式的多個實例,但使用不同的配置。假設應用程式是在函數內部建立的,並且您可以呼叫該函數來實例化它,那麼這真的很容易實現。為了開發您的應用程式以支持在函數中建立新實例,請查看應用程式工廠模式。

一個非常常見的例子是為每個子網域建立應用程式。例如,您將您的網路伺服器配置為將所有子網域的所有請求分派到您的應用程式,然後您使用子網域資訊來建立使用者特定的實例。一旦您設定好伺服器以監聽所有子網域,您就可以使用一個非常簡單的 WSGI 應用程式來進行動態應用程式建立。

在這方面,抽象化的完美層級是 WSGI 層。您編寫自己的 WSGI 應用程式,該應用程式會查看傳入的請求並將其委派給您的 Flask 應用程式。如果該應用程式尚不存在,則會動態建立並記住它。

from threading import Lock

class SubdomainDispatcher:

    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 wsgiref.util import shift_path_info

class PathDispatcher:

    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:
            shift_path_info(environ)
        else:
            app = self.default_app
        return app(environ, start_response)

def _peek_path_info(environ):
    segments = environ.get("PATH_INFO", "").lstrip("/").split("/", 1)
    if segments:
        return segments[0]

    return None

這與子網域分派之間的最大區別在於,如果建立器函數返回 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)