Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/studio/static/doc/flask-docs/_sources/patterns/appdispatch.txt
blob: 93b4af96bc0925457d50bd405052427159def3ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
.. _app-dispatch:

Application Dispatching
=======================

Application dispatching is the process of combining multiple Flask
applications on the WSGI level.  You can not only combine Flask
applications into something larger but any WSGI application.  This would
even 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.

The fundamental difference from the :ref:`module approach
<larger-applications>` 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
--------------------------

Each of the techniques and examples below results in an ``application`` object
that can be run with any WSGI server.  For production, see :ref:`deployment`.
For development, Werkzeug provides a builtin server for development available
at :func:`werkzeug.serving.run_simple`::

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

Note that :func:`run_simple <werkzeug.serving.run_simple>` is not intended for
use in production.  Use a :ref:`full-blown WSGI server <deployment>`.


Combining Applications
----------------------

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
dispatched based on prefix.

For example you could have your main application run on `/` and your
backend interface on `/backend`::

    from werkzeug.wsgi import DispatcherMiddleware
    from frontend_app import application as frontend
    from backend_app import application as backend

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


Dispatch by Subdomain
---------------------

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 instanciate 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
:ref:`app-factories` pattern.

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.

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
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)


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
----------------

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)

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)