在 FastAPI 中等待 request.json() 永远挂起

Gen*_*ror 4 python freeze exceptionhandler starlette fastapi

我将此处给出的异常处理(https://github.com/tiangolo/fastapi/discussions/6678)添加到我的代码中,但我想打印完整的请求正文以查看完整的内容。但是,当我等待时,request.json()它永远不会终止。request.json()返回一个协程,因此我需要等待协程完成才能打印结果。如果向端点发送了无效请求,如何打印请求内容?

来自 github 的代码示例,我在错误处理程序和一个简单的端点中进行了 2 处更改。

import logging
from fastapi import FastAPI, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModel

app = FastAPI()


@app.exception_handler(RequestValidationError)
async def validation_exception_handler(
    request: Request, exc: RequestValidationError
) -> JSONResponse:
    exc_str = f"{exc}".replace("\n", " ").replace("   ", " ")
    logging.error(f"{request}: {exc_str}")
    body = await request.json()  # This line was added by me and never completes
    logging.error(body)  # This line was added by me
    content = {"status_code": 10422, "message": exc_str, "data": None}
    return JSONResponse(
        content=content, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY
    )


class User(BaseModel):
    name: str


@app.post("/")
async def test(body: User) -> User:
    return body

Run Code Online (Sandbox Code Playgroud)

Chr*_*ris 5

awaiting时request.json,您正在尝试从流中读取request正文(而不是response您可能假设的正文);但是,此操作已您的 API 端点中进行(在您的情况下,在幕后)。因此,await request.json()在 API 的请求生命周期中只允许调用一次,并且尝试再次读取它将会无限期地等待(因为读取请求正文已耗尽),这会导致挂起问题。您可能会发现这个答案对于在将请求传递到端点之前在 FastAPI/Starlette 中间件中读取/记录request正文(和/或正文)也很有帮助。response

至于从RequestValidationError异常处理程序中的响应中获取错误正文,您不需要读取正文request,而是可以通过调用, 而不是 来response读取正文,如此处和此处所示。请参阅这些答案以了解更多详细信息。exc.bodyrequest.json()

例子

from fastapi.exceptions import RequestValidationError

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    return JSONResponse(
        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        content=jsonable_encoder({"detail": exc.errors(),  # optionally include the errors
                "body": exc.body,
                 "custom msg": {"Your error message"}}),
    )
Run Code Online (Sandbox Code Playgroud)