JavaRush /Курсы /Модуль 4: FastAPI /Валидация данных, полученных от внешних API

Валидация данных, полученных от внешних API

Модуль 4: FastAPI
16 уровень , 5 лекция
Открыта

Однако взаимодействие с внешними API — это не только запросы, но и работа с ответами. Сервисы могут возвращать данные некорректного формата, пропускать ключи или даже вовсе отвечать чем-то неприличным (например, ошибкой). Сегодня мы обсудим, как валидировать эти данные в FastAPI с помощью библиотеки Pydantic. Да, это тот самый Pydantic, который мы уже мельком видели в контексте создания моделей для ввода данных.

Давайте представим, что вы заказали пиццу онлайн. Вы ожидаете увидеть в коробке аппетитный кусок теста с сыром и начинкой, но вместо этого вам принесли суп. В программировании такая неожиданность (ну, почти) может произойти, если внешнее API вдруг вернет данные не того формата, который вы ожидали.

Вот несколько причин, почему важна валидация данных:

  1. Гарантия структуры данных: без валидации вы не можете быть уверены, что данные будут в нужном формате.
  2. Обработка ошибок: если данные не соответствуют ожиданиям, вы можете заранее предупредить приложение об этом и не допустить падения.
  3. Безопасность: валидация предотвращает использование некорректных данных, которые могут привести к уязвимостям.

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

Советы по валидации данных

  1. Создавайте гибкие модели: используйте Optional для необязательных полей и дефолтные значения.
  2. Проверяйте ключи: всегда предполагайте, что ключи могут отсутствовать.
  3. Обрабатывайте вложенные структуры: если данные сложные, разбивайте их на отдельные модели.
  4. Логируйте ошибки: это помогает находить проблемы во внешних API.

Теперь вы знаете, как валидировать данные от внешних API в FastAPI. Эти знания помогут вам создавать надежные и устойчивые приложения, которые не рухнут при первой встрече с "неожиданной" погодой или странными данными.

1
Задача
Модуль 4: FastAPI, 16 уровень, 5 лекция
Недоступна
Простая валидация JSON-ответа
Простая валидация JSON-ответа
1
Задача
Модуль 4: FastAPI, 16 уровень, 5 лекция
Недоступна
Валидация вложенных данных
Валидация вложенных данных
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ