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. Не забувай читати документацію зовнішніх сервісів і тестувати кожну інтеграцію, щоб уникнути несподіваних проблем.

3
Опитування
Основи REST і GraphQL API, рівень 16, лекція 4
Недоступний
Основи REST і GraphQL API
Основи REST і GraphQL API
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ