mirror of
https://github.com/LukeHagar/connexion.git
synced 2025-12-09 20:37:46 +00:00
Add middleware documentation (#1706)
Contributes towards https://github.com/spec-first/connexion/issues/1531 This PR adds a documentation page on middleware.
This commit is contained in:
@@ -87,12 +87,66 @@ class _Options:
|
|||||||
|
|
||||||
|
|
||||||
class MiddlewarePosition(enum.Enum):
|
class MiddlewarePosition(enum.Enum):
|
||||||
|
"""Positions to insert a middleware"""
|
||||||
|
|
||||||
BEFORE_SWAGGER = SwaggerUIMiddleware
|
BEFORE_SWAGGER = SwaggerUIMiddleware
|
||||||
|
"""Add before the :class:`SwaggerUIMiddleware`. This is useful if you want your changes to
|
||||||
|
affect the Swagger UI, such as a path altering middleware that should also alter the paths
|
||||||
|
exposed by the Swagger UI
|
||||||
|
|
||||||
|
Be mindful that security has not yet been applied at this stage.
|
||||||
|
|
||||||
|
Since the inserted middleware is positioned before the RoutingMiddleware, you cannot leverage
|
||||||
|
any routing information yet and should implement your middleware to work globally instead of on
|
||||||
|
an operation level.
|
||||||
|
|
||||||
|
:meta hide-value:
|
||||||
|
"""
|
||||||
BEFORE_ROUTING = RoutingMiddleware
|
BEFORE_ROUTING = RoutingMiddleware
|
||||||
|
"""Add before the :class:`RoutingMiddleware`. This is useful if you want your changes to be
|
||||||
|
applied before hitting the router, such as for path altering or CORS middleware.
|
||||||
|
|
||||||
|
Be mindful that security has not yet been applied at this stage.
|
||||||
|
|
||||||
|
Since the inserted middleware is positioned before the RoutingMiddleware, you cannot leverage
|
||||||
|
any routing information yet and should implement your middleware to work globally instead of on
|
||||||
|
an operation level.
|
||||||
|
|
||||||
|
:meta hide-value:
|
||||||
|
"""
|
||||||
BEFORE_SECURITY = SecurityMiddleware
|
BEFORE_SECURITY = SecurityMiddleware
|
||||||
|
"""Add before the :class:`SecurityMiddleware`. Insert middleware here that needs to be able to
|
||||||
|
adapt incoming requests before security is applied.
|
||||||
|
|
||||||
|
Be mindful that security has not yet been applied at this stage.
|
||||||
|
|
||||||
|
Since the inserted middleware is positioned after the RoutingMiddleware, you can leverage
|
||||||
|
routing information and implement the middleware to work on an individual operation level.
|
||||||
|
|
||||||
|
:meta hide-value:
|
||||||
|
"""
|
||||||
BEFORE_VALIDATION = RequestValidationMiddleware
|
BEFORE_VALIDATION = RequestValidationMiddleware
|
||||||
|
"""Add before the :class:`RequestValidationMiddleware`. Insert middleware here that needs to be
|
||||||
|
able to adapt incoming requests before they are validated.
|
||||||
|
|
||||||
|
Since the inserted middleware is positioned after the RoutingMiddleware, you can leverage
|
||||||
|
routing information and implement the middleware to work on an individual operation level.
|
||||||
|
|
||||||
|
:meta hide-value:
|
||||||
|
"""
|
||||||
BEFORE_CONTEXT = ContextMiddleware
|
BEFORE_CONTEXT = ContextMiddleware
|
||||||
|
"""Add before the :class:`ContextMiddleware`, near the end of the stack. This is the default
|
||||||
|
location. The inserted middleware is only followed by the ContextMiddleware, which ensures any
|
||||||
|
changes to the context are properly exposed to the application.
|
||||||
|
|
||||||
|
Since the inserted middleware is positioned after the RoutingMiddleware, you can leverage
|
||||||
|
routing information and implement the middleware to work on an individual operation level.
|
||||||
|
|
||||||
|
Since the inserted middleware is positioned after the ResponseValidationMiddleware,
|
||||||
|
it can intercept responses coming from the application and alter them before they are validated.
|
||||||
|
|
||||||
|
:meta hide-value:
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class API:
|
class API:
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ Documentation
|
|||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
quickstart
|
quickstart
|
||||||
|
middleware
|
||||||
cli
|
cli
|
||||||
routing
|
routing
|
||||||
request
|
request
|
||||||
|
|||||||
205
docs/middleware.rst
Normal file
205
docs/middleware.rst
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
Middleware
|
||||||
|
==========
|
||||||
|
|
||||||
|
Connexion is built as an ASGI middleware stack wrapping an application. It includes several
|
||||||
|
middlewares by default that add functionality based on the OpenAPI specification, in the
|
||||||
|
following order:
|
||||||
|
|
||||||
|
.. csv-table::
|
||||||
|
:widths: 30, 70
|
||||||
|
|
||||||
|
**ExceptionMiddleware**, Handles exceptions raised by the middleware stack or application
|
||||||
|
**SwaggerUIMiddleware**, Adds a Swagger UI to your application
|
||||||
|
**RoutingMiddleware**, "Routes incoming requests to the right operation defined in the
|
||||||
|
specification"
|
||||||
|
**SecurityMiddleware**, "Checks incoming requests against the security defined in the
|
||||||
|
specification"
|
||||||
|
**RequestValidationMiddleware**, Validates the incoming requests against the spec
|
||||||
|
**ResponseValidationMiddleware**, "Validates the returned responses against the spec, if
|
||||||
|
activated"
|
||||||
|
**LifespanMiddleware**, "Allows registration of code to run before application start-up or
|
||||||
|
after shut-down"
|
||||||
|
**ContextMiddleware**, "Makes several request scoped context variables available to the
|
||||||
|
application"
|
||||||
|
|
||||||
|
Adding middleware
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
You can easily add additional ASGI middleware to the middleware stack with the
|
||||||
|
:code:`add_middleware` method:
|
||||||
|
|
||||||
|
.. tab-set::
|
||||||
|
|
||||||
|
.. tab-item:: AsyncApp
|
||||||
|
:sync: AsyncApp
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from connexion import AsyncApp
|
||||||
|
|
||||||
|
app = AsyncApp(__name__)
|
||||||
|
|
||||||
|
app.add_middleware(MiddlewareClass, **options)
|
||||||
|
|
||||||
|
.. dropdown:: View a detailed reference of the :code:`add_middleware` method
|
||||||
|
:icon: eye
|
||||||
|
|
||||||
|
.. automethod:: connexion.AsyncApp.add_middleware
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
.. tab-item:: FlaskApp
|
||||||
|
:sync: FlaskApp
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from connexion import FlaskApp
|
||||||
|
|
||||||
|
app = FlaskApp(__name__)
|
||||||
|
|
||||||
|
app.add_middleware(MiddlewareClass, **options)
|
||||||
|
|
||||||
|
.. dropdown:: View a detailed reference of the :code:`add_middleware` method
|
||||||
|
:icon: eye
|
||||||
|
|
||||||
|
.. automethod:: connexion.FlaskApp.add_middleware
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
.. tab-item:: ConnexionMiddleware
|
||||||
|
:sync: ConnexionMiddleware
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from asgi_framework import App
|
||||||
|
from connexion import ConnexionMiddleware
|
||||||
|
|
||||||
|
app = App(__name__)
|
||||||
|
app = ConnexionMiddleware(app)
|
||||||
|
|
||||||
|
app.add_middleware(MiddlewareClass, **options)
|
||||||
|
|
||||||
|
.. dropdown:: View a detailed reference of the :code:`add_middleware` method
|
||||||
|
:icon: eye
|
||||||
|
|
||||||
|
.. automethod:: connexion.ConnexionMiddleware.add_middleware
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
Middleware order
|
||||||
|
****************
|
||||||
|
|
||||||
|
The :code:`add_middleware` method takes a :code:`position` argument to define where in the
|
||||||
|
middleware stack it should be inserted, which should be an instance of the
|
||||||
|
:class:`~connexion.middleware.MiddlewarePosition` Enum. The positions below are ordered from
|
||||||
|
outer to inner, in the order they are hit by incoming requests. Note that responses hit the
|
||||||
|
middlewares in reversed order.
|
||||||
|
|
||||||
|
.. autoclass:: connexion.middleware.MiddlewarePosition
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
|
Customizing the middleware stack
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
If you need more flexibility, or want to modify or delete any of the default middlewares, you can
|
||||||
|
also pass in a customized middleware stack when instantiating your application.
|
||||||
|
|
||||||
|
For example, if you would like to remove the :class:`SecurityMiddleware` since you are handling
|
||||||
|
Security through an API Gateway in front of your application, you can do:
|
||||||
|
|
||||||
|
.. tab-set::
|
||||||
|
|
||||||
|
.. tab-item:: AsyncApp
|
||||||
|
:sync: AsyncApp
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from connexion import AsyncApp, ConnexionMiddleware
|
||||||
|
|
||||||
|
middlewares = [middleware for middleware in ConnexionMiddleware.default_middlewares
|
||||||
|
if not isinstance(middleware, SecurityMiddleware)]
|
||||||
|
|
||||||
|
app = AsyncApp(__name__, middlewares=middlewares)
|
||||||
|
|
||||||
|
.. dropdown:: View a detailed reference of the :class:`~connexion.AsyncApp`
|
||||||
|
:code:`__init__` method
|
||||||
|
:icon: eye
|
||||||
|
|
||||||
|
.. autoclass:: connexion.AsyncApp
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
.. tab-item:: FlaskApp
|
||||||
|
:sync: FlaskApp
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from connexion import FlaskApp, ConnexionMiddleware
|
||||||
|
|
||||||
|
middlewares = [middleware for middleware in ConnexionMiddleware.default_middlewares
|
||||||
|
if not isinstance(middleware, SecurityMiddleware)]
|
||||||
|
|
||||||
|
app = FlaskApp(__name__, middlewares=middlewares)
|
||||||
|
|
||||||
|
.. dropdown:: View a detailed reference of the :class:`~connexion.FlaskApp`
|
||||||
|
:code:`__init__` method
|
||||||
|
:icon: eye
|
||||||
|
|
||||||
|
.. autoclass:: connexion.FlaskApp
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
|
||||||
|
.. tab-item:: ConnexionMiddleware
|
||||||
|
:sync: ConnexionMiddleware
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from asgi_framework import App
|
||||||
|
from connexion import ConnexionMiddleware
|
||||||
|
|
||||||
|
middlewares = [middleware for middleware in ConnexionMiddleware.default_middlewares
|
||||||
|
if not isinstance(middleware, SecurityMiddleware)]
|
||||||
|
|
||||||
|
app = App(__name__)
|
||||||
|
app = ConnexionMiddleware(app, middlewares=middlewares)
|
||||||
|
|
||||||
|
.. dropdown:: View a detailed reference of the :class:`~connexion.ConnexionMiddleware`
|
||||||
|
:code:`__init__` method
|
||||||
|
:icon: eye
|
||||||
|
|
||||||
|
.. autoclass:: connexion.ConnexionMiddleware
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
|
||||||
|
Writing custom middleware
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
You can add any custom middleware as long as it implements the ASGI interface. To learn how to
|
||||||
|
write pure ASGI middleware, please refer to the `documentation of starlette`_.
|
||||||
|
|
||||||
|
List of useful middleware
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Starlette provides a bunch of useful middleware such as:
|
||||||
|
|
||||||
|
* `CORSMiddleware`_
|
||||||
|
* `SessionMiddleware`_
|
||||||
|
* `HTTPSRedirectMiddleware`_
|
||||||
|
* `TrustedHostMiddleware`_
|
||||||
|
* `GZipMiddleware`_
|
||||||
|
|
||||||
|
Other useful middleware:
|
||||||
|
|
||||||
|
* `ProxyHeadersMiddleware`_ by Uvicorn
|
||||||
|
* `SentryASGIMiddleware`_ by Sentry
|
||||||
|
* `MetricsMiddleware`_ by Prometheus
|
||||||
|
|
||||||
|
For more, check the `asgi-middleware topic`_ on github.
|
||||||
|
|
||||||
|
.. _documentation of starlette: https://www.starlette.io/middleware/#writing-pure-asgi-middleware
|
||||||
|
.. _CORSMiddleware: https://www.starlette.io/middleware/#corsmiddleware
|
||||||
|
.. _SessionMiddleware: https://www.starlette.io/middleware/#sessionmiddleware
|
||||||
|
.. _HTTPSRedirectMiddleware: https://www.starlette.io/middleware/#httpsredirectmiddleware
|
||||||
|
.. _TrustedHostMiddleware: https://www.starlette.io/middleware/#trustedhostmiddleware
|
||||||
|
.. _GZipMiddleware: https://www.starlette.io/middleware/#gzipmiddleware
|
||||||
|
.. _ProxyHeadersMiddleware: https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py
|
||||||
|
.. _SentryASGIMiddleware: https://docs.sentry.io/platforms/python/configuration/integrations/asgi/
|
||||||
|
.. _MetricsMiddleware: https://github.com/claws/aioprometheus/blob/master/src/aioprometheus/asgi/middleware.py
|
||||||
|
.. _asgi-middleware topic: https://github.com/topics/asgi-middleware
|
||||||
Reference in New Issue
Block a user