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

Hob*_* C. 4 python starlette 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)
Run Code Online (Sandbox Code Playgroud)

客户端代码如下:

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)
Run Code Online (Sandbox Code Playgroud)

rag*_*ria 5

我还没有解决这个问题的方法,但是,我花了相当多的时间陷入这个悬而未决的问题(对于我的组织中具有多个自定义 MDW 的关键应用程序)。这种挂起基本上是因为@app.middleware("http")基于中间件,是在后端通过继承 Starlette 的BaseHTTPMiddleware. 所以对于显式继承编写的MDW也存在这个问题BaseHTTPMiddleware。造成这种情况的原因相当复杂,这是我目前所了解的:

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

最后一个链接还提到,导致挂起问题的原因是StreamingResponse响应的读取在第一次读取时不知何故耗尽,当涉及第二次读取时,它会无限期地等待它,这会导致挂起。(这里的第一个和第二个意思是:在 ASGI 应用程序中,消息以各种类型发送到客户端和应用程序http.response.starthttp.response.body例如等)