encode / starlette

The little ASGI framework that shines. 🌟
https://www.starlette.io/
BSD 3-Clause "New" or "Revised" License
10.26k stars 933 forks source link

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

Closed LoadingZhang closed 1 month ago

LoadingZhang commented 3 months ago

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

adriangb commented 3 months ago

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()?

LoadingZhang commented 3 months ago

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 commented 3 months ago

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

Kludex commented 1 month ago

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.