为什么在添加中间件打印HTTP请求体时fastapi会挂起



我使用中间件打印HTTP请求体,以避免在每个函数中出现print语句。但是,在运行客户端代码时,fastapi没有响应。

服务器简化为以下代码:

import typing
import uvicorn
from fastapi import FastAPI
from fastapi import Request, Body
app = FastAPI()

@app.middleware('http')
async def debug_request(request: Request, call_next):
_body = await request.body()
print(_body)
#
response = await call_next(request)
return response

@app.put("/")
def _(
_body: typing.Dict
):
# print(_body)  # this statement is replaced by the middleware
return {"detail": "ok"}

if __name__ == '__main__':
uvicorn.run(app, host='localhost', port=8000)

客户端代码如下:

import requests
_url = 'http://localhost:8000/'
_json = {
'row_id': '1'
}
resp = requests.put(_url, json=_json)
if not resp.ok:
print('http-code: ', resp.status_code)
print('http-response: ', resp.text)

我还没有解决方案,但我已经花了相当多的时间来解决这个悬而未决的问题(对于我组织中具有多个自定义MDW的关键应用程序(。这种挂起主要是因为基于@app.middleware("http")的中间件,是在后台继承Starlette的BaseHTTPMiddleware创建的。因此,显式继承BaseHTTPMiddleware编写的MDW也存在这个问题。造成这种情况的原因非常复杂,这就是我迄今为止所理解的:

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

最后一个链接还提到,同样导致挂起问题的是,由于StreamingResponse;响应的读取在第一次读取时不知何故会耗尽,当它进入第二次读取时,它会无限期地等待它,从而导致挂起。(这里的第一个和第二个意思是:在ASGI应用程序中,消息被发送到具有各种类型的客户端和应用程序,如http.response.starthttp.response.body等(

最新更新