-
-
Notifications
You must be signed in to change notification settings - Fork 6.1k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Code after yield in the async dependecy executed before response has been sent to the client #5068
Comments
Could you please post your code in which you tried native ASGI middleware? You say you tried it, but didn't solve the issue, can we assume that it yielded the same behaviour as with BaseHTTPMiddleware? I would highly recommend to use native ASGI middleware anyway, as BaseHTTPMiddleware |
This is actually much stranger behaviour than I would expect to be honest. I wrote the ASGI middleware myself, and then the code after yield is even immediately called after the response has been sent (and before it has been handled by the middleware). I thought "maybe it is the asyncio.sleep, which is throwing back control to the event loop" but that made no difference at all. Code: import asyncio
from typing import AsyncGenerator
import uvicorn
import logging
from fastapi import FastAPI, Depends
from starlette.requests import Request
from starlette.responses import Response
logger = logging.getLogger()
app = FastAPI(debug=True)
class BarMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
return await self.app(scope, receive, send)
logger.warning("BAR: Before call self.app()")
status_code = 1
async def send_wrapper(message):
if message["type"] == "http.response.start":
nonlocal status_code
status_code = message["status"]
await send(message)
r = await self.app(scope, receive, send_wrapper)
logger.warning("BAR: After call self.app()")
logger.warning(f"BAR: Status code {status_code}")
logger.warning("BAR: Before sleep()")
#await asyncio.sleep(2)
logger.warning("BAR: After sleep()")
class ZxcMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
return await self.app(scope, receive, send)
logger.warning("ZXC: Before call self.app()")
status_code = 1
async def send_wrapper(message):
if message["type"] == "http.response.start":
nonlocal status_code
status_code = message["status"]
await send(message)
r = await self.app(scope, receive, send_wrapper)
logger.warning("ZXC: After call self.app()")
logger.warning(f"ZXC: Status code {status_code}")
logger.warning("ZXC: Before sleep()")
#await asyncio.sleep(2)
logger.warning("ZXC: After sleep()")
async def get_foo() -> AsyncGenerator[str, None]:
logger.warning("BEFORE YIELD FOO")
yield "foo"
logger.warning("AFTER YIELD FOO")
async def get_qwerty(foo: str = Depends(get_foo)) -> AsyncGenerator[str, None]:
logger.warning("BEFORE YIELD QWERTY")
yield "qwerty"
logger.warning("AFTER YIELD QWERTY")
app.add_middleware(BarMiddleware)
app.add_middleware(ZxcMiddleware)
@app.get("/")
async def root(qwerty: str = Depends(get_qwerty)):
return {"message": qwerty}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000, ) My logging:
This doesn't make sense to me at all to be honest. I am really curious to your ASGI middleware, and the behaviour in that. |
That's what I have tried.
|
That is using httpbasemiddleware as well? (The decorator ‘ @app.middleware("http")’ is just syntax sugarcoating for inheriting from BaseHTTPMiddleware. |
My bad, sorry, you're right. It works as I would expect with a real native middleware. Thank you.
But I still believe that either docs need to be updated, or there must be a disclaimer to avoid using BaseHttpMiddleware. |
Interesting, I thought that was the wrong order. The docs say:
So, I figured the
would appear as latest in the logging stack. Maybe I am interpreting the documentation incorrectly, because the code after By the way, that is why my response was that it was strange behaviour, funny that you expected that very behaviour your self 😋 |
So from my understanding, it looks correct, response was sent already, we got back to our dependencies. The difference here is that we're actually logging in the middleware AFTER the response has been sent, which was not possible using BaseHttpMiddleware. But I don't see a problem here. Docs do not specify when we should get to that point, before or after deps. In our case we're only interested in commiting / rolling back the transaction. It can be done in the place of "After sleep" log message (in the |
That's not for sure yet... There has been a lot of discussion about That being said... They are still limitations, and if you are going to have them, for the time being, pure ASGI middlewares are recommended. |
Fair point, I corrected my comment above 😇 |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
First Check
Commit to Help
Example Code
Description
Hi.
From the documentation:
But this is not the case:
Response has not been sent to the client yet, but we're already in the "after yield" code. I can not reproduce it with a single middleware though.
Using native ASGI middlewares (instead of inheriting from BaseHTTPMiddleware) does not solve the issue.
It's a problem for us, because we can not rely on the fact that transaction has been commited already (in the middleware), when we're in the "after yield" code.
Either docs are misleading, or something works incorrectly.
Operating System
Linux
Operating System Details
No response
FastAPI Version
0.78.0
Python Version
3.8.10
Additional Context
No response
The text was updated successfully, but these errors were encountered: