Skip to content

Commit 60de3f2

Browse files
committed
Updated appdispatch pattern
1 parent 45b97d1 commit 60de3f2

File tree

1 file changed

+94
-18
lines changed

1 file changed

+94
-18
lines changed

docs/patterns/appdispatch.rst

Lines changed: 94 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,61 @@
33
Application Dispatching
44
=======================
55

6+
Application dispatching is the process of combining multiple Flask
7+
applications on the WSGI level. You can not only combine Flask
8+
applications into something larger but any WSGI application. This would
9+
even allow you to run a Django and a Flask application in the same
10+
interpreter side by side if you want. The usefulness of this depends on
11+
how the applications work internally.
12+
13+
This is fundamentally different from the :ref:`module approach
14+
<larger-applications>` is that in this case you are running the same or
15+
different Flask applications that are entirely isolated from each other.
16+
They run different configurations and are dispatched on the WSGI level.
17+
18+
Combining Applications
19+
----------------------
20+
21+
If you have entirely separated applications and you want them to work next
22+
to each other in the same Python interpreter process you can take
23+
advantage of the :class:`werkzeug.wsgi.DispatcherMiddleware`. The idea
24+
here is that each Flask application is a valid WSGI application and they
25+
are combined by the dispatcher middleware into a larger one that
26+
dispatched based on prefix.
27+
28+
For example you could have your main application run on `/` and your
29+
backend interface on `/admin`::
30+
31+
from werkzeug.wsgi import DispatcherMiddleware
32+
from frontend_app import application as frontend
33+
from backend_app import application as backend
34+
35+
application = DispatcherMiddleware(frontend, {
36+
'/backend': backend
37+
})
38+
39+
40+
Dispatch by Subdomain
41+
---------------------
42+
643
Sometimes you might want to use multiple instances of the same application
744
with different configurations. Assuming the application is created inside
845
a function and you can call that function to instanciate it, that is
946
really easy to implement. In order to develop your application to support
1047
creating new instances in functions have a look at the
1148
:ref:`app-factories` pattern.
1249

13-
14-
Dispatch by Subdomain
15-
---------------------
16-
1750
A very common example would be creating applications per subdomain. For
1851
instance you configure your webserver to dispatch all requests for all
1952
subdomains to your application and you then use the subdomain information
20-
to create user-specific instances.
53+
to create user-specific instances. Once you have your server set up to
54+
listen on all subdomains you can use a very simple WSGI application to do
55+
the dynamic application creation.
2156

22-
Once you have your server set up to listen on all subdomains you can use a
23-
very simple WSGI application to do the dynamic application creation.
24-
25-
The code for the dispatching looks roughly like this:
26-
27-
.. sourcecode:: python
57+
The perfect level for abstraction in that regard is the WSGI layer. You
58+
write your own WSGI application that looks at the request that comes and
59+
and delegates it to your Flask application. If that application does not
60+
exist yet, it is dynamically created and remembered::
2861

2962
from threading import Lock
3063

@@ -43,21 +76,16 @@ The code for the dispatching looks roughly like this:
4376
with self.lock:
4477
app = self.instances.get(subdomain)
4578
if app is None:
46-
app = self.make_app(subdomain)
79+
app = self.create_app(subdomain)
4780
self.instances[subdomain] = app
4881
return app
4982

50-
def make_app(self, subdomain):
51-
return self.create_app(subdomain)
52-
5383
def __call__(self, environ, start_response):
5484
app = self.get_application(environ['HTTP_HOST'])
5585
return app(environ, start_response)
5686

5787

58-
If you want to use it, you can do something like this:
59-
60-
.. sourcecode:: python
88+
This dispatcher can then be used like this::
6189

6290
from myapplication import create_app, get_user_for_subdomain
6391
from werkzeug.exceptions import NotFound
@@ -76,3 +104,51 @@ If you want to use it, you can do something like this:
76104
return create_app(user)
77105

78106
application = SubdomainDispatcher('example.com', make_app)
107+
108+
109+
Dispatch by Path
110+
----------------
111+
112+
Dispatching by a path on the URL is very similar. Instead of looking at
113+
the `Host` header to figure out the subdomain one simply looks at the
114+
request path up to the first slash::
115+
116+
from threading import Lock
117+
from werkzeug.wsgi import pop_path_info, peek_path_info
118+
119+
class PathDispatcher(object):
120+
121+
def __init__(self, default_app, create_app):
122+
self.default_app = default_app
123+
self.create_app = create_app
124+
self.lock = Lock()
125+
self.instances = {}
126+
127+
def get_application(self, prefix):
128+
with self.lock:
129+
app = self.instances.get(prefix)
130+
if app is None:
131+
app = self.create_app(prefix)
132+
if app is not None:
133+
self.instances[prefix] = app
134+
return app
135+
136+
def __call__(self, environ, start_response):
137+
app = self.get_application(peek_path_info(environ))
138+
if app is not None:
139+
pop_path_info(environ)
140+
else:
141+
app = self.default_app
142+
return app(environ, start_response)
143+
144+
The big difference between this and the subdomain one is that this one
145+
falls back to another application if the creator function returns `None`::
146+
147+
from myapplication import create_app, default_app, get_user_for_prefix
148+
149+
def make_app(prefix):
150+
user = get_user_for_prefix(prefix)
151+
if user is not None:
152+
return create_app(user)
153+
154+
application = PathDispatcher('example.com', default_app, make_app)

0 commit comments

Comments
 (0)