Application Dispatching

Application dispatching is the process of combining multiple Flaskapplications on the WSGI level. You can combine not only Flaskapplications but any WSGI application. This would allow you to run aDjango and a Flask application in the same interpreter side by side ifyou want. The usefulness of this depends on how the applications workinternally.

The fundamental difference from the module approach is that in this case you are running the same ordifferent 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 objectthat can be run with any WSGI server. For production, see Deployment Options.For development, Werkzeug provides a builtin server for development availableat werkzeug.serving.run_simple():

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

Note that run_simple is not intended foruse in production. Use a full-blown WSGI server.

In order to use the interactive debugger, debugging must be enabled both onthe application and the simple server. Here is the “hello world” example withdebugging and run_simple:

  1. from flask import Flask
  2. from werkzeug.serving import run_simple
  3.  
  4. app = Flask(__name__)
  5. app.debug = True
  6.  
  7. @app.route('/')
  8. def hello_world():
  9. return 'Hello World!'
  10.  
  11. if __name__ == '__main__':
  12. run_simple('localhost', 5000, app,
  13. use_reloader=True, use_debugger=True, use_evalex=True)

Combining Applications

If you have entirely separated applications and you want them to work nextto each other in the same Python interpreter process you can takeadvantage of the werkzeug.wsgi.DispatcherMiddleware. The ideahere is that each Flask application is a valid WSGI application and theyare combined by the dispatcher middleware into a larger one that isdispatched based on prefix.

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

  1. from werkzeug.wsgi import DispatcherMiddleware
  2. from frontend_app import application as frontend
  3. from backend_app import application as backend
  4.  
  5. application = DispatcherMiddleware(frontend, {
  6. '/backend': backend
  7. })

Dispatch by Subdomain

Sometimes you might want to use multiple instances of the same applicationwith different configurations. Assuming the application is created insidea function and you can call that function to instantiate it, that isreally easy to implement. In order to develop your application to supportcreating new instances in functions have a look at theApplication Factories pattern.

A very common example would be creating applications per subdomain. Forinstance you configure your webserver to dispatch all requests for allsubdomains to your application and you then use the subdomain informationto create user-specific instances. Once you have your server set up tolisten on all subdomains you can use a very simple WSGI application to dothe dynamic application creation.

The perfect level for abstraction in that regard is the WSGI layer. Youwrite your own WSGI application that looks at the request that comes anddelegates it to your Flask application. If that application does notexist yet, it is dynamically created and remembered:

  1. from threading import Lock
  2.  
  3. class SubdomainDispatcher(object):
  4.  
  5. def __init__(self, domain, create_app):
  6. self.domain = domain
  7. self.create_app = create_app
  8. self.lock = Lock()
  9. self.instances = {}
  10.  
  11. def get_application(self, host):
  12. host = host.split(':')[0]
  13. assert host.endswith(self.domain), 'Configuration error'
  14. subdomain = host[:-len(self.domain)].rstrip('.')
  15. with self.lock:
  16. app = self.instances.get(subdomain)
  17. if app is None:
  18. app = self.create_app(subdomain)
  19. self.instances[subdomain] = app
  20. return app
  21.  
  22. def __call__(self, environ, start_response):
  23. app = self.get_application(environ['HTTP_HOST'])
  24. return app(environ, start_response)

This dispatcher can then be used like this:

  1. from myapplication import create_app, get_user_for_subdomain
  2. from werkzeug.exceptions import NotFound
  3.  
  4. def make_app(subdomain):
  5. user = get_user_for_subdomain(subdomain)
  6. if user is None:
  7. # if there is no user for that subdomain we still have
  8. # to return a WSGI application that handles that request.
  9. # We can then just return the NotFound() exception as
  10. # application which will render a default 404 page.
  11. # You might also redirect the user to the main page then
  12. return NotFound()
  13.  
  14. # otherwise create the application for the specific user
  15. return create_app(user)
  16.  
  17. application = SubdomainDispatcher('example.com', make_app)

Dispatch by Path

Dispatching by a path on the URL is very similar. Instead of looking atthe Host header to figure out the subdomain one simply looks at therequest path up to the first slash:

  1. from threading import Lock
  2. from werkzeug.wsgi import pop_path_info, peek_path_info
  3.  
  4. class PathDispatcher(object):
  5.  
  6. def __init__(self, default_app, create_app):
  7. self.default_app = default_app
  8. self.create_app = create_app
  9. self.lock = Lock()
  10. self.instances = {}
  11.  
  12. def get_application(self, prefix):
  13. with self.lock:
  14. app = self.instances.get(prefix)
  15. if app is None:
  16. app = self.create_app(prefix)
  17. if app is not None:
  18. self.instances[prefix] = app
  19. return app
  20.  
  21. def __call__(self, environ, start_response):
  22. app = self.get_application(peek_path_info(environ))
  23. if app is not None:
  24. pop_path_info(environ)
  25. else:
  26. app = self.default_app
  27. return app(environ, start_response)

The big difference between this and the subdomain one is that this onefalls back to another application if the creator function returns None:

  1. from myapplication import create_app, default_app, get_user_for_prefix
  2.  
  3. def make_app(prefix):
  4. user = get_user_for_prefix(prefix)
  5. if user is not None:
  6. return create_app(user)
  7.  
  8. application = PathDispatcher(default_app, make_app)