Модель ответа FastAPI
Модели ответа используются для объявления структуры данных, возвращаемых API. Объявляя модель ответа, 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 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 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 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_id | response | Описание |
|---|---|---|
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 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