I just started a project with starlette awesome, and found a small issue about the docs related on the order of execution of middlewares... My case, is so usual, like in django:
auth_middleware that depends on session_middleware
Seems like the order is inverse on the order you declare them:
app.add_middleware(AuthenticationMiddleware, backend=middlewares.AuthBackend())
app.add_middleware(middlewares.SessionMiddleware)
The session one is executed first but needs to be added later. I'm not against it, it's an onion, and can be seen in both directions... But I thought that at least should be documented.
On the API side, perhaps is a nice idea, to just instantiate them on a single call, like:
app.add_middlewares([
middlewareX, middlewareY # in order of execution... (at least when the request is entering..)
])
Yeah, I'm not a fan of how it is at the moment, but it's a bit unavoidable with the current style. (We can only wrap middleware around what's already been added.)
And yes, we'll eventually do something more like this...
middleware = [
Middleware(TrustedHostMiddleware, allowed_hosts=ALLOWED_HOSTS),
Middleware(HTTPSRedirectMiddleware, enabled=not DEBUG),
Middleware(SessionMiddleware, backend=CookieSignedSessions(secret_key=SECRET_KEY)),
Middleware(AuthMiddleware, backend=DatabaseAuthBackend(model=User)),
]
app = Starlette(
debug=DEBUG,
routes=routes,
events=events,
middleware=middleware,
exception_handlers=exception_handlers
)
Something like this?
class App(Starlette):
async def __call__(self, scope, receive, send):
scope['app'] = self
# middlewares could be a list
# and also some kind of predicates could be added
# to just at runtime, change the middlware chain.
# or the result, could just be cached.
result = compose(
self.router,
(TransactionMiddleware, ),
(ExceptionMiddleware, dict(debug=self.debug)),
(ServerErrorMiddleware, dict(debug=self.debug))
)
await result(scope, receive, send)
def compose(initial, *args):
base = initial
for arg in args:
if len(arg) == 2:
klass, kwargs = arg
base = klass(base, **kwargs)
elif len(arg) == 1:
klass = arg[0]
base = klass(base)
return base
app = App(debug=True)
had the same issue, just adding some documentation would be great as this is a pretty opaque bug to fix.
Thank you so much @jordic !! Should definitely be in the doc imo.
Most helpful comment
had the same issue, just adding some documentation would be great as this is a pretty opaque bug to fix.