Middleware
The middleware pipeline is configured with the middleware
configuration property.
It must contain a list of functions that receive the next app in the pipeline, the
settings object and the dependency injection container and must return a plain asgi
middleware instance:
def middleware_factory(app, settings, di):
async def inner(scope, receive, send):
await app(scope, receive, send)
return inner
Any asgi middleware can be used in the middleware pipeline. For instance, it is possible to use the SessionMiddleware from starlette:
Usage
To demonstrate the middleware system, we will create a timing middleware that will output to the console the time spent in the processing of the request:
from collections.abc import Callable
from datetime import datetime
import structlog
from selva.di import service
logger = structlog.get_logger()
def timing_middleware(app, settings, di):
async def inner(scope, receive, send):
request_start = datetime.now()
await app(scope, receive, send)
request_end = datetime.now()
delta = request_end - request_start
logger.info("request duration", duration=str(delta))
return inner
Middleware dependencies
Middleware functions can use the provided dependency injection container to get services the middleware might need. We could rewrite the timing middleware to persist the timings using a service instead of printing to the console:
from datetime import datetime
from application.service import TimingService
class TimingMiddleware:
def __init__(self, app, timing_service: TimingService):
self.timing_service = timing_service
async def __call__(self, scope, receive, send):
request_start = datetime.now()
await app(scope, receive, send)
request_end = datetime.now()
await self.timing_service.save(request_start, request_end)
async def timing_middleware(app, settings, di):
timing_service = await di.get(TimingService)
return TimingMiddleware(app, timing_service)