starlette icon indicating copy to clipboard operation
starlette copied to clipboard

request body get empty when using middleware to save request to contextvar

Open LoadingZhang opened this issue 1 year ago • 3 comments
trafficstars

I want to save request to ContextVar so I can use it anywhere. Here is code sample:

from contextvars import ContextVar

import uvicorn
from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request

ctx: ContextVar[Request] = ContextVar("request")
app = FastAPI()


class RequestContextMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        token = ctx.set(request)
        try:
            response = await call_next(request)
        finally:
            ctx.reset(token)
        return response

app.add_middleware(RequestContextMiddleware)

async def root(request: Request, body: str):
    print(1, request._stream_consumed)
    print(2, request._body)
    ctx_request = ctx.get()
    print(3, ctx_request._body)


app.add_api_route("/", root, methods=["POST"])

if __name__ == '__main__':
    uvicorn.run(app)

Output:

1 True
2 "the request data"
...
    print(ctx_request._body)
          ^^^^^^^^^^^^^^^^^
AttributeError: '_CachedRequest' object has no attribute '_body'. Did you mean: 'body'?

so, my question is

  • Why middleware will read body?
  • Why the ctx_request._body get empty, but the request._body in param is worked

LoadingZhang avatar Aug 13 '24 16:08 LoadingZhang

I'm not sure I understood your question / problem but _body is a private attribute that you should not be accessing or interacting with. Maybe try await request.body()?

adriangb avatar Aug 13 '24 17:08 adriangb

I'm not sure I understood your question / problem but _body is a private attribute that you should not be accessing or interacting with. Maybe try await request.body()?

@adriangb I used await request.body() before, but it raised an error:

raise RuntimeError("Stream consumed")
RuntimeError: Stream consumed

After investigation, I found out that _body was missing and _stream_consumed is True after middleware processed.

LoadingZhang avatar Aug 13 '24 17:08 LoadingZhang

And I discovered similar topic in the discussion, which might be related. https://github.com/encode/starlette/discussions/2556

LoadingZhang avatar Aug 13 '24 17:08 LoadingZhang

Let's continue the discussion there then.

This doesn't seem an issue, also the script on the description is using private attributes that shouldn't be used to demonstrate an issue.

Kludex avatar Sep 29 '24 18:09 Kludex