← Назад к вопросам

Какие типы ответов можно возвращать в FastAPI?

2.3 Middle🔥 251 комментариев
#FastAPI и Flask

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Типы ответов в FastAPI

FastAPI предоставляет гибкую систему для возврата разных типов ответов. Расскажу о каждом.

1. Базовый тип (dict, list)

Автоматически преобразуется в JSON:

from fastapi import FastAPI

app = FastAPI()

@app.get("/users")
def get_users():
    return {
        "status": "ok",
        "users": [
            {"id": 1, "name": "Alice"},
            {"id": 2, "name": "Bob"}
        ]
    }

# Response: {"status": "ok", "users": [{"id": 1, "name": "Alice"}, ...]}

2. Pydantic модели (рекомендуется)

Основной способ с типизацией и валидацией:

from pydantic import BaseModel
from typing import Optional
from datetime import datetime

class User(BaseModel):
    id: int
    name: str
    email: str
    age: Optional[int] = None
    created_at: datetime

class PaginatedUsers(BaseModel):
    total: int
    page: int
    users: list[User]

@app.get("/users", response_model=PaginatedUsers)
def get_users(page: int = 1):
    return PaginatedUsers(
        total=100,
        page=page,
        users=[
            User(id=1, name="Alice", email="alice@mail.com", created_at=datetime.now())
        ]
    )

# Автоматическая валидация, документация Swagger

3. Список моделей

Возвращаем массив объектов:

class UserResponse(BaseModel):
    id: int
    name: str
    email: str

@app.get("/users", response_model=list[UserResponse])
def list_users():
    return [
        UserResponse(id=1, name="Alice", email="alice@mail.com"),
        UserResponse(id=2, name="Bob", email="bob@mail.com"),
    ]

4. JSON Response

Для явного контроля:

from fastapi.responses import JSONResponse

@app.get("/users/{user_id}")
def get_user(user_id: int):
    if user_id < 0:
        return JSONResponse(
            status_code=400,
            content={"detail": "Invalid user_id"}
        )
    
    return JSONResponse(
        status_code=200,
        content={"id": user_id, "name": "Alice"}
    )

5. HTMLResponse

Возвращение HTML:

from fastapi.responses import HTMLResponse

@app.get("/", response_class=HTMLResponse)
def get_html():
    return """
    <html>
        <body>
            <h1>Welcome to API</h1>
        </body>
    </html>
    """

6. FileResponse

Отправка файлов:

from fastapi.responses import FileResponse
from pathlib import Path

@app.get("/download")
def download_file():
    file_path = Path("./documents/report.pdf")
    return FileResponse(
        path=file_path,
        media_type='application/pdf',
        filename="report.pdf"
    )

# Или с streaming для больших файлов
@app.get("/download-large")
def download_large():
    return FileResponse(
        path="./videos/movie.mp4",
        media_type='video/mp4',
        filename="movie.mp4"
    )

7. StreamingResponse

Для потока данных (видео, большие файлы):

import asyncio
from fastapi.responses import StreamingResponse

async def generate_data():
    for i in range(10):
        await asyncio.sleep(0.1)
        yield f"data: chunk {i}\n".encode()

@app.get("/stream")
async def stream_data():
    return StreamingResponse(
        generate_data(),
        media_type="text/event-stream"
    )

8. PlainTextResponse

Обычный текст:

from fastapi.responses import PlainTextResponse

@app.get("/text", response_class=PlainTextResponse)
def get_text():
    return "Hello, World!"

9. RedirectResponse

Перенаправление:

from fastapi.responses import RedirectResponse

@app.get("/old-path")
def redirect_to_new():
    return RedirectResponse(url="/new-path", status_code=301)

10. Custom Response

Пользовательский ответ с заголовками:

from fastapi.responses import JSONResponse

@app.get("/custom")
def custom_response():
    return JSONResponse(
        status_code=200,
        content={"message": "Success"},
        headers={
            "X-Custom-Header": "custom-value",
            "Cache-Control": "no-cache"
        }
    )

11. Union типы (несколько возможных ответов)

from typing import Union

class SuccessResponse(BaseModel):
    id: int
    name: str

class ErrorResponse(BaseModel):
    detail: str
    code: int

@app.get("/users/{user_id}", response_model=Union[SuccessResponse, ErrorResponse])
def get_user(user_id: int):
    if user_id < 1:
        return ErrorResponse(detail="Invalid ID", code=400)
    return SuccessResponse(id=user_id, name="Alice")

12. Асинхронные ответы

from fastapi import BackgroundTasks

class ReportResponse(BaseModel):
    report_id: str
    status: str

@app.post("/generate-report")
async def generate_report(background_tasks: BackgroundTasks):
    report_id = "report_123"
    
    # Отправить ответ сразу
    background_tasks.add_task(slow_report_generation, report_id)
    
    return ReportResponse(
        report_id=report_id,
        status="processing"
    )

async def slow_report_generation(report_id: str):
    # Это выполнится в фоне
    await asyncio.sleep(5)
    print(f"Report {report_id} generated")

13. Множество моделей ответов

from fastapi import status

class User(BaseModel):
    id: int
    name: str

class ErrorDetail(BaseModel):
    detail: str

@app.get(
    "/users/{user_id}",
    response_model=User,
    responses={
        200: {"description": "User found"},
        404: {"model": ErrorDetail, "description": "User not found"},
        500: {"model": ErrorDetail, "description": "Internal error"},
    }
)
def get_user(user_id: int):
    if user_id < 1:
        return JSONResponse(
            status_code=404,
            content={"detail": "User not found"}
        )
    return User(id=user_id, name="Alice")

14. Кастомные заголовки ответов

from fastapi import Response

@app.get("/custom-headers")
def custom_headers(response: Response):
    response.headers["X-Token"] = "secret-token"
    response.headers["Cache-Control"] = "max-age=3600"
    return {"message": "OK"}

15. Cookies в ответе

@app.get("/login")
def login(response: Response):
    response.set_cookie(
        key="session_id",
        value="abc123xyz",
        max_age=3600,
        secure=True,
        httponly=True
    )
    return {"status": "logged in"}

16. Status codes

from fastapi import status

@app.post("/users", status_code=status.HTTP_201_CREATED)
def create_user(user: User):
    # Автоматически вернёт 201
    return user

@app.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_user(user_id: int):
    # 204 No Content
    return None

Рекомендуемые практики

# ✅ ХОРОШО — используй Pydantic модели
from pydantic import BaseModel

class UserResponse(BaseModel):
    id: int
    name: str
    email: str

@app.get("/users/{user_id}", response_model=UserResponse)
def get_user(user_id: int):
    return UserResponse(id=user_id, name="Alice", email="alice@mail.com")

# ✅ ХОРОШО — обработка ошибок
from fastapi import HTTPException

@app.get("/users/{user_id}")
def get_user_safe(user_id: int):
    if user_id < 1:
        raise HTTPException(status_code=404, detail="User not found")
    return UserResponse(id=user_id, name="Alice", email="alice@mail.com")

# ✅ ХОРОШО — документация в Swagger
class CreateUserRequest(BaseModel):
    name: str
    email: str

@app.post("/users", response_model=UserResponse)
def create_user(user: CreateUserRequest):
    """Создаёт нового пользователя."""
    return UserResponse(id=1, name=user.name, email=user.email)

# ❌ ПЛОХО — raw dict без типизации
@app.get("/users")
def get_users():
    return {"id": 1, "name": "Alice"}  # Нет валидации

Чеклист для ответов

  1. Используй response_model — автоматическая валидация
  2. Явно задай status_code — 201 для создания, 204 для удаления
  3. Документируй responses — для Swagger
  4. Обрабатывай ошибки через HTTPException — красивые ошибки
  5. Добавляй заголовки для кэширования — если нужно
  6. Используй Union для множественных типов ответов
  7. Streaming для больших файлов — не грузи всё в память
  8. Background tasks — для долгих операций

FastAPI автоматически сериализует Pydantic модели в JSON и генерирует OpenAPI документацию. Это главное его преимущество перед Flask.