JavaRush /Курсы /Модуль 4: FastAPI /Обработка POST-запросов: отправка данных во внешние API

Обработка POST-запросов: отправка данных во внешние API

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

Асинхронные GET-запросы – это прекрасно, конечно, но что делать, если случай требует от нас больше, чем просто получить данные? Иногда нам нужно отправить данные на сервер. И тут на сцену выходят POST-запросы! Сегодня мы разберём, как их отправлять, как передавать данные в формате JSON или FormData, и как грамотно обрабатывать результаты.

POST-запросы – это, по сути, просьба к серверу что-то сделать с переданными данными. Например, добавить в базу новый объект или создать новый ресурс.

Каковы отличия между GET- и POST-запросами?

GET-запросы строго ориентированы на чтение данных – они не изменяют состояние сервера. POST-запросы, напротив, меняют серверное состояние, поскольку они чаще всего используются для создания или модификации ресурсов.

Если сравнить их по аналогии с обычной человеческой жизнью, то GET – это как посмотреть в меню ресторана, а POST – это сделать заказ. Задача POST-запроса – отправить детали (например, заказанное блюдо) на сервер и получить подтверждение.


Отправка асинхронных POST-запросов

Теперь, когда мы понимаем теорию, давайте перейдём к практике. Для отправки POST-запросов мы будем использовать библиотеку httpx, которая уже стала нашим верным спутником.

Начнём с базы, создадим простой асинхронный POST-запрос и передадим JSON-данные.

Пример: отправка JSON-объекта

Предположим, мы хотим отправить пользователя с данными на внешний API.


import httpx
from fastapi import FastAPI

app = FastAPI()

# Асинхронный POST-запрос
@app.post("/send-data")
async def send_data():
    url = "https://jsonplaceholder.typicode.com/posts"  # Тестовый доступный API
    data = {
        "title": "Hello FastAPI",
        "body": "FastAPI makes integration so easy!",
        "userId": 1
    }

    async with httpx.AsyncClient() as client:
        response = await client.post(url, json=data)

    return {"status": response.status_code, "response": response.json()}

Что здесь происходит?

  1. Мы создаём JSON-данные с помощью Python-словаря data.
  2. Вызов client.post() отправляет асинхронный запрос. Ключ json автоматически сериализует словарь в формат JSON.
  3. Ответ от сервера (response) обрабатывается. Мы возвращаем его статус и JSON-ответ обратно.
Примечание:

Для тестирования мы использовали https://jsonplaceholder.typicode.com – это популярный тестовый API, который выдает фейковые данные.


Настройка заголовков и тела запроса

Иногда API требует заголовки (например, Content-Type, Authorization). Вот пример отправки POST-запроса с добавлением заголовков:


@app.post("/send-data-with-headers")
async def send_data_with_headers():
    url = "https://jsonplaceholder.typicode.com/posts"
    data = {
        "title": "Header Example",
        "body": "Including headers in POST request",
        "userId": 2
    }

    headers = {"Authorization": "Bearer some_token_123"}

    async with httpx.AsyncClient() as client:
        response = await client.post(url, json=data, headers=headers)

    return {"status": response.status_code, "response": response.json()}

Здесь мы добавили заголовок Authorization для аутентификации. Это привычное требование многих API – не забудьте внимательно прочитать документацию внешнего сервиса.


Передача данных в формате FormData

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

FastAPI использует Starlette под капотом, а httpx позволяет легко работать с формами.

Пример: отправка FormData


from fastapi import Form

@app.post("/send-form-data")
async def send_form_data():
    url = "https://jsonplaceholder.typicode.com/posts"
    f_data = { 
        "field1": "value1",
        "field2": "value2"
    }

    async with httpx.AsyncClient() as client:
        response = await client.post(url, data=f_data)  # Используем `data` вместо `json`

    return {"status": response.status_code, "response": response.text}

Ключевая разница здесь в том, что мы используем параметр data, а не json. httpx преобразует данные в формат application/x-www-form-urlencoded, что соответствует стандарту FormData.


Практические примеры

Давайте представим реальный сценарий. Мы строим приложение для управления задачами, и нам нужно отправить данные новой задачи на внешний API.


@app.post("/tasks")
async def create_task(title: str = Form(...), description: str = Form(None)):
    url = "https://yourtaskmanager.com/api/tasks"
    data = {
        "title": title,
        "description": description,
        "userId": 42
    }

    async with httpx.AsyncClient() as client:
        try:
            response = await client.post(url, json=data)
            response.raise_for_status()  # Выбросить исключение, если статус-код не в диапазоне 200-299
        except httpx.RequestError as exc:
            return {"error": f"An error occurred while requesting: {exc}"}
        except httpx.HTTPStatusError as exc:
            return {"error": f"HTTP error occurred: {exc.response.status_code}"}
            
    return {"task_id": response.json().get("id"), "message": "Task created successfully"}

При интеграции с внешними сервисами следует быть готовым к неожиданным сбоям:

  • API может быть временно недоступен.
  • Вы можете случайно отправить некорректные данные.
  • У API могут быть ограничения (например, rate limit).

Для обработки таких ситуаций мы использовали конструкцию try-except.


Настройка логирования и отладки

Чтобы понять, что происходит при ошибках, полезно добавить логирование:


import logging

logging.basicConfig(level=logging.INFO)

@app.post("/log-example")
async def log_example():
    url = "https://example.com/api"
    data = {"key": "value"}

    logging.info("Отправка данных на %s с телом %s", url, data)

    async with httpx.AsyncClient() as client:
        response = await client.post(url, json=data)

    logging.info("Ответ сервера: %s", response.status_code)
    return response.json()

Заключительный пример: реальная интеграция

Мы собрали всё в единый пример. Этот эндпоинт:

  1. Получает данные от пользователя (например, название и описание задачи).
  2. Отправляет данные на внешний API.
  3. Возвращает клиенту ответ от API.

@app.post("/external-task")
async def external_task(title: str = Form(...), description: str = Form(None)):
    external_api_url = "https://example.com/api/tasks"
    headers = {"Authorization": "Bearer super_secret_token"}
    body = {"title": title, "description": description}

    async with httpx.AsyncClient() as client:
        response = await client.post(external_api_url, json=body, headers=headers)

    return {"status": response.status_code, "response": response.json()}

Теперь у вас есть всё, что нужно для отправки POST-запросов! POST открывает огромные возможности интеграции FastAPI с внешними системами – от отправки данных форм до сложных взаимодействий с RESTful API. Не забывайте изучать документацию внешних сервисов и тестировать каждую интеграцию, чтобы избежать неожиданных проблем.

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