Уявіть, що ваш сервер — це нічний клуб, а вхідні дані — це люди, які намагаються потрапити на тусовку. Якщо ви не перевіряєте, хто заходить (вік, наявність квитка тощо), скоро почнеться хаос. У випадку з API хаос — це помилки, вразливості й поломка системи. Валідація вхідних даних дозволяє впевнитися, що сервер отримує саме ті дані, яких він очікує. Вона захищає від випадкового введення неправильних даних користувачем і навіть від навмисних спроб злому.
У FastAPI Pydantic моделі дають нам змогу легко автоматизувати цю перевірку і зробити її не тільки безпечною, а й зручною.
Використання Pydantic моделей для валідації запитів
Давайте почнемо з простого прикладу: ми хочемо створити endpoint, який приймає дані користувача (ім'я, вік і email) і повертає їх назад, якщо вони пройшли перевірку.
Приклад
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
# Створюємо Pydantic-модель для валідації даних
class User(BaseModel):
name: str
age: int
email: EmailStr
@app.post("/users/")
async def create_user(user: User):
# Якщо дані в user пройшли валідацію — повертаємо їх
return user
Пояснення:
- Ми створили клас
User, успадкований відBaseModel. Ця модель визначає схему вхідних даних. - Для поля
emailми використали спеціальний типEmailStrз Pydantic, який автоматично перевіряє, що переданий рядок є коректною email-адресою. - В endpoint ми приймаємо
userяк аргумент функції — FastAPI автоматично перевіряє, що передані дані відповідають моделіUser.
Тепер, якщо ви надішлете запит з некоректними даними:
curl -X POST "http://127.0.0.1:8000/users/" -H "Content-Type: application/json" -d '{"name": "Alice", "age": "not_a_number", "email": "not_an_email"}'
Ви отримаєте відповідь:
{
"detail": [
{
"loc": ["body", "age"],
"msg": "value is not a valid integer",
"type": "type_error.integer"
},
{
"loc": ["body", "email"],
"msg": "value is not a valid email address",
"type": "value_error.email"
}
]
}
FastAPI сам піклується про перевірку даних і повертає докладні повідомлення про помилки.
Передача даних через тіло запиту
Pydantic моделі ідеально підходять для даних, переданих через тіло запиту (Request Body). FastAPI автоматично очікує JSON-структуру, що відповідає моделі.
Приклад: реєстрація книги
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# Модель даних для книги
class Book(BaseModel):
title: str
author: str
pages: int
@app.post("/books/")
async def add_book(book: Book):
return {"message": f"Book '{book.title}' has been successfully added!"}
Спробуємо надіслати запит:
curl -X POST "http://127.0.0.1:8000/books/" -H "Content-Type: application/json" -d '{"title": "1984", "author": "George Orwell", "pages": 328}'
Відповідь:
{
"message": "Book '1984' has been successfully added!"
}
Спробуйте надіслати запит з некоректними даними, наприклад, пропустивши поле title. FastAPI миттєво повідомить вас про це:
{
"detail": [
{
"loc": ["body", "title"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
Використання Pydantic для обробки Query Parameters
Не тільки дані в тілі запиту заслуговують валідації. Часто потрібно перевіряти дані, передані через параметри запиту (Query Parameters).
Приклад з Query Parameters
Уявімо, що користувач хоче знайти книгу по її автору. Зробимо endpoint, який приймає параметр author і використовує Pydantic для перевірки.
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/books/")
async def get_books(author: Annotated[str, Query(min_length=3, max_length=50)]):
return {"message": f"Пошук книг автора: {author}"}
Переваги використання Annotated:
- Явне визначення метаданих:
Annotatedдозволяє чітко розділяти тип даних і пов'язані з ним метадані, що покращує читабельність і підтримку коду. - Безпека типів: Використання
Annotatedдопомагає уникнути потенційних помилок, пов'язаних з некоректною обробкою значень за замовчуванням.
Рекомендується використовувати Annotated для більш явного і безпечного визначення параметрів у FastAPI. Проте, якщо ви віддаєте перевагу коротшому запису і впевнені в коректній обробці значень за замовчуванням, варіант без Annotated також припустимий.
Спробуємо запит:
curl -X GET "http://127.0.0.1:8000/books/?author=Orwell"
Успішна відповідь:
{
"message": "Searching books by author: Orwell"
}
Якщо довжина параметра author буде менше 3 символів, FastAPI поверне помилку:
{
"detail": [
{
"loc": ["query", "author"],
"msg": "ensure this value has at least 3 characters",
"type": "value_error.any_str.min_length",
"ctx": {"limit_value": 3}
}
]
}
Валідація Path Parameters
У FastAPI використовують Path Parameters для передачі ідентифікаторів і інших даних у URL. Pydantic дозволяє також валідовувати такі параметри.
Наведемо приклад. Реалізуємо отримання книги по ID.
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/books/{book_id}")
async def get_book(book_id: int = Path(..., ge=1)):
return {"message": f"Book with ID {book_id}"}
Пояснення:
- Використовуючи
Path(...), ми задаємо обмеження на параметр шляху. У цьому випадкуbook_idмає бути цілим числом більше або рівним 1 (ge=1). - Якщо переданий ідентифікатор не відповідає цим вимогам, FastAPI поверне повідомлення про помилку.
Спробуємо запит:
curl -X GET "http://127.0.0.1:8000/books/5"
Успішна відповідь:
{
"message": "Book with ID 5"
}
Якщо передати некоректний ID, наприклад, -3, результат буде таким:
{
"detail": [
{
"loc": ["path", "book_id"],
"msg": "ensure this value is greater than or equal to 1",
"type": "value_error.number.not_ge",
"ctx": {"limit_value": 1}
}
]
}
Типові помилки та особливості
Працюючи з валідацією в Query, Path і Request Body, важливо пам'ятати:
- Якщо ви забули вказати поле як обов'язкове (
...), FastAPI автоматично зробить його опційним зі значеннямNone. - Порядок аргументів в endpoint (Path, Query, Body) має значення. FastAPI використовує їх для розділення даних.
- Не забувайте про вбудовані типи Pydantic, такі як
EmailStr,IPv4Address,PositiveInt— вони полегшують життя.
Тепер, коли ви знаєте, як валідовувати вхідні дані, ви впевнено можете сказати, що ви — досвідчений вишибала, який не пропустить погані дані на закриту API-вечірку.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ