Globally importable request object
See original GitHub issueHello, I had an issue with Starlette where I had to access request information outside of a view function. I successfully fixed my issue and would like to share the discussion I had with @tomchristie and how I solved the issue.
Here’s a snippet of the question I asked and the answer I got:
From me:
Hi!
I am currently working with Starlette (porting over a Flask project to FastAPI) and I’m wondering if there’s some way to get the current request from anywhere in the code, like you can do with Flask using from flask import request
.
I need to access data from the request but from a non-view part of the code (a logging filter, so I can’t even pass it along).
I’ve read the whole documentation and looked at all the GitHub issues but couldn’t find anything that fits my needs, this https://github.com/encode/starlette/issues/379 is the closest I found, which seems to be part of what I want. However I found no way of importing the request object so it’s useless to me.
Reply: No we don’t currently do that. Using contextvars would be the way to implement it. You could do this in a middleware class without having to alter Starlette directly. Happy to chat through how you’d achieve that, but let’s open a ticket if there’s more to talk over here. 😃
Solution
RequestContextMiddleware:
from contextvars import ContextVar
from uuid import uuid4
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request
REQUEST_ID_CTX_KEY = "request_id"
_request_id_ctx_var: ContextVar[str] = ContextVar(REQUEST_ID_CTX_KEY, default=None)
def get_request_id() -> str:
return _request_id_ctx_var.get()
class RequestContextMiddleware(BaseHTTPMiddleware):
async def dispatch(
self, request: Request, call_next: RequestResponseEndpoint
):
request_id = _request_id_ctx_var.set(str(uuid4()))
response = await call_next(request)
_request_id_ctx_var.reset(request_id)
return response
In your app initialisation:
app.add_middleware(RequestContextMiddleware)
Usage:
Now you will be able to import the get_request_id
function anywhere and get the current request ID, or None
if you are not in the context of a request.
You can also add many more context vars by creating new ContextVar
objects, and using them the same way as _request_id_ctx_var
in my example.
For the future
Maybe Starlette could expose an object that abstracts the context vars in some way. But my idea on this isn’t clear, I don’t know how generic this could be.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:17
- Comments:10 (6 by maintainers)
Top GitHub Comments
There goes my first PR in OSS!
https://github.com/encode/starlette/pull/770
Came across a similar issue as @MarcDufresne. I am using FastAPI (thanks @tiangolo), and needed a way to access information on the request object outside of a view. I initially looked at using starlette-context (thanks @tomwojcik) but found the below solution to work for my needs.
Marc, first off just wanna say thanks so much for this solution! It got me started on the right path. Wanted to post an update here, for anyone having a similar issue in 2022.
Several months after this initial solution, the authors warn against using
BaseHTTPMiddleware
– the parent class Marc’s middleware inherits from.Instead, the suggestion is to use a raw ASGI middleware. However, there isn’t much documentation for this. I was able to use Starlette’s AuthenticationMiddleware as a reference point, and develop what I needed in combination with Marc’s wonderful solution of ContextVars.
And then in the app setup:
And finally, the non-view function:
Thanks again to everyone in this thread for all the help, and I hope the above is useful!