123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778 |
- """
- Application Dispatcher
- ======================
- This middleware creates a single WSGI application that dispatches to
- multiple other WSGI applications mounted at different URL paths.
- A common example is writing a Single Page Application, where you have a
- backend API and a frontend written in JavaScript that does the routing
- in the browser rather than requesting different pages from the server.
- The frontend is a single HTML and JS file that should be served for any
- path besides "/api".
- This example dispatches to an API app under "/api", an admin app
- under "/admin", and an app that serves frontend files for all other
- requests::
- app = DispatcherMiddleware(serve_frontend, {
- '/api': api_app,
- '/admin': admin_app,
- })
- In production, you might instead handle this at the HTTP server level,
- serving files or proxying to application servers based on location. The
- API and admin apps would each be deployed with a separate WSGI server,
- and the static files would be served directly by the HTTP server.
- .. autoclass:: DispatcherMiddleware
- :copyright: 2007 Pallets
- :license: BSD-3-Clause
- """
- import typing as t
- if t.TYPE_CHECKING:
- from _typeshed.wsgi import StartResponse
- from _typeshed.wsgi import WSGIApplication
- from _typeshed.wsgi import WSGIEnvironment
- class DispatcherMiddleware:
- """Combine multiple applications as a single WSGI application.
- Requests are dispatched to an application based on the path it is
- mounted under.
- :param app: The WSGI application to dispatch to if the request
- doesn't match a mounted path.
- :param mounts: Maps path prefixes to applications for dispatching.
- """
- def __init__(
- self,
- app: "WSGIApplication",
- mounts: t.Optional[t.Dict[str, "WSGIApplication"]] = None,
- ) -> None:
- self.app = app
- self.mounts = mounts or {}
- def __call__(
- self, environ: "WSGIEnvironment", start_response: "StartResponse"
- ) -> t.Iterable[bytes]:
- script = environ.get("PATH_INFO", "")
- path_info = ""
- while "/" in script:
- if script in self.mounts:
- app = self.mounts[script]
- break
- script, last_item = script.rsplit("/", 1)
- path_info = f"/{last_item}{path_info}"
- else:
- app = self.mounts.get(script, self.app)
- original_script_name = environ.get("SCRIPT_NAME", "")
- environ["SCRIPT_NAME"] = original_script_name + script
- environ["PATH_INFO"] = path_info
- return app(environ, start_response)
|