question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Consume request body in middleware is problematic

See original GitHub issue
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import PlainTextResponse
from starlette.middleware.base import BaseHTTPMiddleware


class SampleMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        _ = await request.form()
        return await call_next(request)


app = Starlette()


@app.route('/test', methods=['POST'])
async def test(request):
    _ = await request.form()  # blocked, because middleware already consumed request body
    return PlainTextResponse('Hello, world!')


app.add_middleware(SampleMiddleware)
$ uvicorn test:app --reload
$ curl -d "a=1" http://127.0.0.1:8000/test
# request is blocked

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:13
  • Comments:32 (7 by maintainers)

github_iconTop GitHub Comments

53reactions
wyfocommented, Jul 19, 2019

Coming from FastAPI issue referenced above.

@tomchristie I don’t understand the issue about consuming the request in a middleware, could you explain this point ? In fact, I have the need (which is current where I work) to log every requests received by my production server. Is there a better place than a middleware to do it and avoid duplicating code in every endpoint ? (I would like something like this https://github.com/Rhumbix/django-request-logging)

For now, I found this workaround (but that’s not very pretty):

async def set_body(request: Request, body: bytes):
    async def receive() -> Message:
        return {"type": "http.request", "body": body}

    request._receive = receive

async def get_body(request: Request) -> bytes:
    body = await request.body()
    set_body(request, body)
    return body

but there will always be cases where it can’t work (eg. if you stream the request data, then it’s just not going to be available anymore later down the line)

I kind of disagree with your example. In fact, stream data is not stored by default, but stream metadata (is the stream closed) are; there will be an understandable error raised if someone try to stream twice, and that is enough imho. That’s why if the body is cached, the stream consumption has to be cached too.

40reactions
tomchristiecommented, May 20, 2019

“Consume request body in middleware is problematic”

Indeed. Consuming request data in middleware is problematic. Not just to Starlette, but generally, everywhere.

On the whole you should avoid doing so if at all possible.

There’s some work we could do to make it work better, but there will always be cases where it can’t work (eg. if you stream the request data, then it’s just not going to be available anymore later down the line).

Read more comments on GitHub >

github_iconTop Results From Across the Web

Get starlette request body in the middleware context
So the line body = await request.body() freezes all requests that have body and I have 504 from all of them. How can...
Read more >
Consuming the request body — Quart 0.17.0 documentation
The body is sent after the request line and headers for both HTTP/1 and HTTP/2. This allows Quart to trigger the app's request...
Read more >
FAQ — Falcon 3.1.1 documentation
If your request authentication/authorization is performed in a middleware component which rejects requests lacking authorization credentials by raising an ...
Read more >
Common API mistakes and how to avoid them - LogRocket Blog
If within a request handler, the application throws an error, then the Express server may respond with a Content-Type: text/plain and a body...
Read more >
API Reference - Express 4.x
A new body object containing the parsed data is populated on the request object after the middleware (i.e. req.body ), or an empty...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found