Промежуточное ПО FastAPI, заглядывающее в ответы - PullRequest
1 голос
/ 20 марта 2020

Я пытаюсь написать простое промежуточное ПО для FastAPI, просматривающего тела ответов.

В этом примере я просто записываю содержимое тела:

app = FastAPI()

@app.middleware("http")
async def log_request(request, call_next):
    logger.info(f'{request.method} {request.url}')
    response = await call_next(request)
    logger.info(f'Status code: {response.status_code}')
    async for line in response.body_iterator:
        logger.info(f'    {line}')
    return response

Однако, похоже, что я "потребляю" тело таким образом, приводящее к этому исключению:

  ...
  File ".../python3.7/site-packages/starlette/middleware/base.py", line 26, in __call__
    await response(scope, receive, send)
  File ".../python3.7/site-packages/starlette/responses.py", line 201, in __call__
    await send({"type": "http.response.body", "body": b"", "more_body": False})
  File ".../python3.7/site-packages/starlette/middleware/errors.py", line 156, in _send
    await send(message)
  File ".../python3.7/site-packages/uvicorn/protocols/http/httptools_impl.py", line 515, in send
    raise RuntimeError("Response content shorter than Content-Length")
RuntimeError: Response content shorter than Content-Length

Пытаясь заглянуть в объект ответа, я не мог найти никакого другого способа прочитать его содержимое. Как правильно это сделать?

1 Ответ

1 голос
/ 29 марта 2020

У меня была аналогичная потребность в промежуточном программном обеспечении FastAPI, и, хотя это не идеально, вот что мы в итоге получили:

app = FastAPI()

@app.middleware("http")
async def log_request(request, call_next):
    logger.info(f'{request.method} {request.url}')
    response = await call_next(request)
    logger.info(f'Status code: {response.status_code}')
    body = b""
    async for chunk in response.body_iterator:
        body += chunk
    # do something with body ...
    return Response(
        content=body,
        status_code=response.status_code,
        headers=dict(response.headers),
        media_type=response.media_type
    )

Имейте в виду, что такая реализация проблематична c с ответами, передающими тело, которое будет не умещается в оперативной памяти вашего сервера (представьте ответ размером 100 ГБ).

В зависимости от того, что делает ваше приложение, вы будете определять, является ли это проблемой или нет.


В случае где некоторые из ваших конечных точек выдают большие ответы, вы можете избежать использования промежуточного программного обеспечения и вместо этого реализовать собственный ApiRoute. У этого пользовательского ApiRoute будет та же проблема с потреблением тела, но вы можете ограничить его использование определенными конечными точками.

Подробнее см. На https://fastapi.tiangolo.com/advanced/custom-request-and-route/

...