spec-first / connexion

Connexion is a modern Python web framework that makes spec-first and api-first development easy.
https://connexion.readthedocs.io/en/latest/
Apache License 2.0
4.47k stars 759 forks source link

Update context throughout middleware stack? #1750

Open RobbeSneyders opened 10 months ago

RobbeSneyders commented 10 months ago

We currently set the Context variables offered to the user in the ContextMiddleware at the end of the stack, which makes them available in view functions and any code called from there.

In code called earlier in the middleware stack, such as the security functions for example, the context is not available.

Each middleware has access to all the information needed to set the context, so we could update the context in each middleware throughout the middleware stack to make it accessible to code running anywhere in the stack during a request.

Note that middlewares handling responses, such as the ResponseValidation, actually need this context when returning the response, so we might have to set and unset it as follows:

def __call__(self, scope, receive, send):
    # Set context variables
    # Logic working on requests
    # Unset context variables
    self.next_app(scope, receive, send)
    # Set context variables
    # Logic working on responses
    # Unset context variables
SpudInNZ commented 10 months ago

Big +1 for this. This issue bit me quite hard when we were trying to move from a non-trivial Connexion 2 codebase to 3. I can't recall the workaround we had to do, but it wasn't pretty.

explody commented 10 months ago

Another big +1 on this. I'm working on a custom authentication scenario that needs access to the request context, to make authn and authz decisions based on data in the request.

chbndrhnns commented 7 months ago

@SpudInNZ May I ask for your workaround? I need access to the request in the security middleware and currently I have no idea how to do that.

pradhan-v commented 4 weeks ago

For middlewares inheriting the starlette.middleware.base.BaseHTTPMiddleware the context and the request object can be accessed with this workaround

from starlette.middleware.base import BaseHTTPMiddleware

class RequestIdMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        request_id = request.headers.get("x-request-id")

        # FIXME: https://github.com/spec-first/connexion/issues/1750
        connexion_context = request.scope.get("extensions", {}).get("connexion_context", {})

        connexion_context["request_id"] = request_id
        response = await call_next(request)
        response.headers["request_id"] = request_id
        return response