Как повторно транслировать ответ от внешнего API?

Вопрос или проблема

Представьте, что существует простое приложение FastAPI, которое имеет один маршрут, который повторно транслирует ответ файла от внешнего API:

from fastapi import FastAPI

app = FastAPI()


@app.get(
    "/download",
    response_class=FileResponse,
    responses={
        200: {"content": {"application/octet-stream": {}}, "description": "Файл"},
    },
)
async def download():
    async def iterfile():
        url = "http://external-api:8000/api/v1/attachments-archive/download?id__in=d6ace33a-c068-45ac-8e7c-60f30f262e09"
        async with AsyncClient() as client:
            async with client.stream("GET", url) as resp:
                # resp.headers возвращает:
                # headers = {
                #     'content-disposition': "attachment;filename*=UTF-8''archive-2024-10-03T21%3A28%3A13.597138.zip",
                #     'content-type': 'application/x-zip-compressed',
                #     ... другие заголовки
                # }
                async for chunk in resp.aiter_bytes():
                    yield chunk
                    
    return StreamingResponse(iterfile())
    # но мне нужно указать (скопировать из resp) заголовки и media_type так:
    # return StreamingResponse(
    #   iterfile(), 
    #   headers={'content-disposition': "attachment;filename*=UTF-8''archive-2024-10-03T21%3A28%3A13.597138.zip"}, 
    #   media_type="application/x-zip-compressed"
    # )

Внешний API возвращает zip-архив. Мне нужно повторно транслировать его. Чтобы транслировать ответ из моего приложения, я использую StreamingResponse.

Основная трудность заключается в том, чтобы указать headers и media_type для StreamingResponse. Как я могу сначала получить эти значения, а затем получить поток iterfile()?

П.С. headers и media_type могут быть разными. Я не могу их захардкодить.

Ответ или решение

Чтобы реализовать restream ответа от внешнего API в вашем приложении на FastAPI, вам нужно сначала получить заголовки и медиаподтип (media type) от ответа внешнего API, а затем передать их в StreamingResponse.

Ниже приведен пример кода, который делает именно это:

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from httpx import AsyncClient

app = FastAPI()

@app.get(
    "/download",
    responses={
        200: {"content": {"application/octet-stream": {}}, "description": "File"},
    },
)
async def download():
    url = "http://external-api:8000/api/v1/attachments-archive/download?id__in=d6ace33a-c068-45ac-8e7c-60f30f262e09"

    async def iterfile():
        async with AsyncClient() as client:
            async with client.stream("GET", url) as resp:
                # Получаем заголовки для ответа
                headers = resp.headers
                # Проверяем, что используем правильные заголовки
                content_disposition = headers.get('content-disposition')
                content_type = headers.get('content-type')

                # Итерация по байтам ответа
                async for chunk in resp.aiter_bytes():
                    yield chunk

    # Создаем потоковый ответ, используя заголовки и медиаподтип, полученные из внешнего API
    return StreamingResponse(
        iterfile(),
        headers={
            'content-disposition': content_disposition, 
            'content-type': content_type
        },
        media_type=content_type
    )

Объяснение кода:

  1. Импорт необходимых библиотек:

    • Мы используем FastAPI и httpx для асинхронного HTTP-запроса к внешнему API.
  2. Определение маршрута /download:

    • Мы прописываем маршрутизацию с использованием HTTP метода GET.
  3. Функция iterfile:

    • В этой вложенной функции мы осуществляем асинхронный запрос к внешнему API.
    • Получаем заголовки ответа (resp.headers), которые содержат информацию, необходимую для корректного формирования ответа (например, content-disposition и content-type).
    • Используем resp.aiter_bytes() для асинхронной итерации по байтам файла.
  4. Возврат StreamingResponse:

    • При создании StreamingResponse передаем iterfile() как источник данных, а также заголовки и медиаподтип, полученные из ответа внешнего API.

Таким образом, данное решение позволяет "репликировать" поток данных от внешнего API в вашем FastAPI приложении, динамически устанавливая необходимые заголовки и тип медиа.

Оцените материал
Добавить комментарий

Капча загружается...