Однак взаємодія з зовнішніми API — це не тільки запити, але й робота з відповідями. Сервіси можуть повертати дані некоректного формату, пропускати ключі або навіть відповідати чимось не тим (наприклад, помилкою). Сьогодні обговоримо, як валідовувати ці дані в FastAPI за допомогою бібліотеки Pydantic. Так, це той самий Pydantic, який ми вже мельком бачили в контексті створення моделей для вводу даних.
Уявімо, що ви замовили піцу онлайн. Очікуєте побачити в коробці апетитний шматок тіста з сиром і начинкою, але натомість принесли суп. У програмуванні така несподіванка (ну, майже) може статись, якщо зовнішнє API раптом поверне дані не того формату, який ви очікували.
Ось кілька причин, чому валідація даних важлива:
- Гарантія структури даних: без валідації ви не можете бути впевненими, що дані в потрібному форматі.
- Обробка помилок: якщо дані не відповідають очікуванням, ви можете заздалегідь попередити додаток і не допустити падіння.
- Безпека: валідація запобігає використанню некоректних даних, які можуть призвести до вразливостей.
FastAPI і Pydantic дають потужні інструменти, щоб дані від зовнішнього API спочатку "пройшли перевірку", перш ніж потрапити в ваш додаток.
Використання Pydantic для валідації
Почнемо з простого прикладу. Припустимо, ми отримуємо дані про погоду з OpenWeatherMap API. Відповідь від API може бути такою:
{
"city": "New York",
"temperature": 22.5,
"unit": "Celsius"
}
Якщо ми не перевіримо дані, вони можуть виглядати "правильно", а можуть містити сюрприз, наприклад:
{
"city_name": "New York",
"temp": "22.5"
}
Це явно не те, що наш код очікував. Використаємо Pydantic для валідації:
Створення Pydantic-моделі
Pydantic-модель — це клас, який наслідується від BaseModel і описує необхідну структуру даних. Наприклад:
from pydantic import BaseModel
class WeatherResponse(BaseModel):
city: str
temperature: float
unit: str
Тут ми очікуємо:
city— рядок з назвою міста.temperature— число з плаваючою крапкою.unit— рядок (наприклад, "Celsius" або "Fahrenheit").
Валідація відповіді від API
Припустимо, що ми зробили запит до зовнішнього API:
import httpx
import asyncio
async def fetch_weather():
async with httpx.AsyncClient() as client:
response = await client.get("https://example.com/weather")
return response.json()
Дані треба валідовувати. Ось як це робиться:
async def get_validated_weather():
raw_data = await fetch_weather()
try:
weather = WeatherResponse(**raw_data)
return weather
except ValueError as e:
print(f"Помилка валідації даних: {e}")
raise
Що тут відбувається? Ми передаємо "сирі" дані через **raw_data у нашу модель WeatherResponse. Якщо структура даних не відповідає очікуванням, Pydantic підніме виключення ValueError.
Робота зі вкладеними структурами
Реальні API часто повертають більш складні об'єкти. Наприклад:
{
"location": {
"city": "New York",
"country": "USA"
},
"weather": {
"temperature": 22.5,
"unit": "Celsius"
}
}
Для обробки таких даних створіть вкладені моделі:
class Location(BaseModel):
city: str
country: str
class Weather(BaseModel):
temperature: float
unit: str
class WeatherApiResponse(BaseModel):
location: Location
weather: Weather
Тепер ви можете працювати з цією відповіддю:
async def get_weather():
raw_data = await fetch_weather()
try:
validated_data = WeatherApiResponse(**raw_data)
return validated_data
except ValueError as e:
print(f"Помилка валідації вкладених даних: {e}")
raise
Робота з опціональними полями
Іноді зовнішні API можуть пропустити деякі ключі. Для таких ситуацій використовуйте Optional:
class WeatherResponse(BaseModel):
city: str
temperature: float | None = None
unit: str
Тепер поле temperature необов'язкове. Якщо його немає в даних, воно встановиться в None.
Приклад інтеграції в FastAPI
З'єднаємо все, що розглянули, в реальний ендпоінт FastAPI:
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/weather")
async def get_weather():
raw_data = await fetch_weather()
try:
validated_data = WeatherApiResponse(**raw_data)
return validated_data
except ValueError:
raise HTTPException(status_code=400, detail="Некоректні дані від API")
Тепер, якщо зовнішнє API поверне некоректні дані, наші користувачі отримають зрозумілу помилку.
Практична реалізація
Коли дані не проходять перевірку, Pydantic автоматично генерує опис помилок. Наприклад:
from pydantic import ValidationError
try:
WeatherResponse(**{"city": "New York", "temperature": "hot", "unit": 123})
except ValidationError as e:
print(e.json())
Вивід:
[
{
"loc": ["temperature"],
"msg": "value is not a valid float",
"type": "type_error.float"
},
{
"loc": ["unit"],
"msg": "str type expected",
"type": "type_error.str"
}
]
Логування помилок
Логуйте всі помилки за допомогою вбудованої бібліотеки logging для подальшого аналізу:
import logging
logging.basicConfig(level=logging.ERROR)
try:
WeatherResponse(**raw_data)
except ValidationError as e:
logging.error(f"Помилка валідації: {e}")
raise
Поради з валідації даних
- Створюйте гнучкі моделі: використовуйте
Optionalдля необов'язкових полів і значення за замовчуванням. - Перевіряйте ключі: завжди припускайте, що ключі можуть бути відсутні.
- Опрацьовуйте вкладені структури: якщо дані складні, розбивайте їх на окремі моделі.
- Логуйте помилки: це допомагає знаходити проблеми у зовнішніх API.
Тепер ви знаєте, як валідовувати дані від зовнішніх API в FastAPI. Ці знання допоможуть створювати надійні й стійкі додатки, які не впадуть при першому зіткненні з "неочікуваною" погодою або дивними даними.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ