Модель ответа FastAPI

Модели ответа используются для объявления структуры данных, возвращаемых API. Объявляя модель ответа, FastAPI автоматически выполняет валидацию, сериализацию и фильтрацию выходных данных, гарантируя, что клиент получает только ожидаемые данные.


Использование аннотаций типа возвращаемого значения

Самый простой способ — напрямую аннотировать тип возвращаемого значения функции операции пути:

Пример

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float


@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: int):
    # Возвращаемые данные будут автоматически отфильтрованы и проверены в соответствии с моделью Item
    return {
        "name": "Foo",
        "description": "Очень хороший Item",
        "price": 22.2,
        "secret": "это не должно быть видно",  # Поля, отсутствующие в Item, будут отфильтрованы
    }

FastAPI использует модель ответа для выполнения следующего:

ОсобенностиОписание
Проверка данныхЕсли возвращаемые данные не соответствуют модели, это указывает на проблему в коде приложения.
Фильтрация данныхВозвращать только поля, определённые в модели, отфильтровывая лишние поля (это важно для безопасности).
Сгенерировать документациюОтображать структуру ответа в документации OpenAPI.
сериализацияИспользовать Pydantic (реализация на Rust) для эффективного преобразования данных в JSON.

параметр response_model

Когда тип возвращаемого значения не соответствует модели ответа, используйте параметр response_model декоратора.response_modelПараметры:

Пример

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


# Входная модель (содержит пароль)
class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr


# Выходная модель (без пароля)
class UserOut(BaseModel):
    username: str
    email: EmailStr


# Функция принимает UserIn, но в ответе используется UserOut
@app.post("/users/", response_model=UserOut)
async def create_user(user: UserIn):
    # Возвращаемый пользователь содержит пароль, но response_model=UserOut отфильтрует пароль.
    return user

Это важный способ защиты конфиденциальных данных. Используя разные входные и выходные модели, такие конфиденциальные поля, как пароли, никогда не появляются в ответах API.


Тип возвращаемого значения и фильтрация данных

Рекомендуется наследование классов для реализации разделения входных/выходных моделей: этот пример позволяет и редактору, и mypy правильно понимать типы, а также позволяет FastAPI фильтровать данные:

Пример

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


# Базовая модель пользователя
class BaseUser(BaseModel):
    username: str
    email: EmailStr


# Входная модель: наследует базовую модель, добавляет поле password
class UserIn(BaseUser):
    password: str


@app.post("/users/")
async def create_user(user: UserIn) -> BaseUser:
    # Аннотация возвращаемого типа — BaseUser, и FastAPI отфильтрует поле password.
    # Редактор и mypy также довольны, потому что UserIn — подкласс BaseUser
    return user

Исключая неустановленные значения по умолчанию

Использованиеresponse_model_exclude_unset=True, возвращать только поля, которые действительно имеют заданные значения:

Пример

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5
    tags: list[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "Бармены", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]

Сравнение ответов для разных item_id:

item_idresponseОписание
foo{"name": "Foo", "price": 50.2}Возвращаются только явно заданные поля, значения по умолчанию опускаются.
bar{"name": "Bar", "description": "...", "price": 62, "tax": 20.2}Все поля имеют фактические значения, поэтому возвращаются все

Связанные параметры фильтра:

ПараметрОписание
response_model_exclude_unsetИсключить поля с неустановленными значениями
response_model_exclude_defaultsИсключить поля со значениями по умолчанию
response_model_exclude_noneИсключить поля со значением None

Включать/исключать конкретные поля

Использованиеresponse_model_includeиresponse_model_excludeТочное управление выходными полями:

Пример

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5


# Содержит только name и description
@app.get("/items/{item_id}", response_model=Item, response_model_include={"name", "description"})
async def read_item_name(item_id: str):
    return items[item_id]


# Исключить налог
@app.get("/items/{item_id}/no-tax", response_model=Item, response_model_exclude={"tax"})
async def read_item_no_tax(item_id: str):
    return items[item_id]

Хотяresponse_model_includeиresponse_model_excludeМожно быстро фильтровать поля, но вместо этого рекомендуется использовать несколько классов моделей, поскольку они не могут удалить исключённые поля из документации OpenAPI.


Итог

  • Использованиеresponse_modelАннотации параметров или возвращаемого типа объявляют модель ответа.
  • Основная роль модели ответа — фильтрация данных, защищающая конфиденциальные данные от утечки.
  • Используйте разные модели для ввода и вывода, чтобы гарантировать, что такие поля, как пароли, не появляются в ответе.
  • response_model_exclude_unsetВозвращать только те поля, которые действительно заданы
  • Рекомендуется использовать наследование моделей + аннотации возвращаемого типа, а неresponse_model_include/exclude
Другие расширения