首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >阻止代码,同时使用中间件和依赖项注入来记录FastAPI(Python)中的请求

阻止代码,同时使用中间件和依赖项注入来记录FastAPI(Python)中的请求
EN

Stack Overflow用户
提问于 2021-08-18 09:59:54
回答 1查看 367关注 0票数 1

拜托,帮我查一下封锁代码的问题。我正在使用FastAPI。

我已经完成了一个中间件来记录传入的请求和它们的服务器响应。

代码语言:javascript
运行
复制
@app.middleware("http")
async def log_request(request: Request, call_next):

    # Code to log incoming request
    response = await call_next(request)
    # Code to log response 
    return response

我有一个没有依赖注入的端点:

代码语言:javascript
运行
复制
@app.post("/without-dependency-injection", tags=["Test"])
async def check_response_without_using_dependency():

    # ... Any code
    return {"token":"token123"}

下面是一个控制台,其中包含一个由中间件编写的日志:

代码语言:javascript
运行
复制
[17.08.2021 13:08:25.08S]: {"event_type": "Incoming request", "id": "3fb33dc0-cb86-49e9-9a0f-c48596e58061"}
[17.08.2021 13:08:25.08S]: {"event_type": "Outgoing response", "id": "3fb33dc0-cb86-49e9-9a0f-c48596e58061"}
INFO:     127.0.0.1:50285 - "POST /without-dependency-injection HTTP/1.1" 200 OK

一切都很顺利!中间件捕获请求,用请求细节写入日志,执行请求,用服务器响应写入日志,并向客户端返回响应。

当我添加依赖项时,行为将发生变化。

这是一个具有依赖项注入的端点:

代码语言:javascript
运行
复制
@app.post("/generate-token/", tags=["Test"])
async def create_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
    
    # ...
    # Code to generate token

    return {"token":f"{token}"}

下面是一个控制台日志:

代码语言:javascript
运行
复制
[17.08.2021 13:13:08.13S]: {"event_type": "Incoming request", "id": "3b6398de-5b20-40ad-820e-24733425e6c7"}

正如您所看到的,没有“传出响应”事件。中间件捕获请求,记录传入的请求数据,并将请求发送到执行。密码被封锁了..。直到服务器收到新请求。只有在该客户端从服务器获得响应之后。

如果我删除了中间件或依赖项注入,一切都会正常工作。

谁知道为什么代码会被阻塞,直到服务器从客户端获得一个新的请求?如何纠正这种行为?

EN

回答 1

Stack Overflow用户

发布于 2022-04-09 19:32:08

基本信息、根本原因与咆哮

嘿,我花了相当多的时间在这个悬而未决的问题上(在我的组织中有多个自定义MDW的关键应用程序)。这基本上是因为基于@app.middleware("http")的中间件是从Starlette的BaseHTTPMiddleware继承而来的后端产品。因此,通过显式继承BaseHTTPMiddleware编写的MDW也存在这个问题。其原因相当复杂,我一直未能完全理解这些理由,但这是我迄今所理解的:

  1. 来自这里(GitHub Starlette杂志)在这里(Github问题):我了解到这个方法使用的是StreamingResponse,这有一些问题
  2. 这里(GitHub Starlette杂志):我了解到挂起的原因之一是:在API中的请求生命周期中只允许等待request.json()一次,而BaseHTTPMiddleware也会自己创建一个请求对象(这会导致挂起问题,因为这是另一个请求)。

最后一个链接还提到,也会导致挂起问题的原因是,由于StreamingResponse;响应的读取在第一次读取时不知怎么地被耗尽了,当涉及到第二次读取时,它一直在无限期地等待它,从而导致挂起。(这里第一次和第二次阅读意味着:在ASGI应用程序中,消息通过各种类型的客户端和应用程序(如http.response.starthttp.response.body等)发送。)

解决方案(部分,记录响应,但我将更新时,我也了解到请求)

所以,不要使用任何与BaseHTTPMiddleware有关的东西。为了解决这一问题,我使用这里提供的ASGI规范编写了我的所有定制中间件

您可以使您的自定义中间件如下所示:

代码语言:javascript
运行
复制
from starlette.types import ASGIApp, Receive, Send, Message

class LogResponseMDW:

    def __init__(self, app: ASGIApp) -> None:
        self.app = app
    
    async def __call__(self, scope: Scope, receive: Receive, send: Send):

        async def send_wrapper(message: Message):
            # This will capture response coming from APP Layer
            # You will want to check for message["type"] first
            # response body will be in message where the type is
            # "http.response.body"
            print(f"message: {message}") # log here, after checking the message type
            await send(message)
        
        await self.app(scope, receive, send_wrapper)


# you can add this to your app this way:
app.add_middleware(LogResponseMDW)
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68830274

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档