Асинхронные 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()}
Что здесь происходит?
- Мы создаём JSON-данные с помощью Python-словаря
data. - Вызов
client.post()отправляет асинхронный запрос. Ключjsonавтоматически сериализует словарь в формат JSON. - Ответ от сервера (
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()
Заключительный пример: реальная интеграция
Мы собрали всё в единый пример. Этот эндпоинт:
- Получает данные от пользователя (например, название и описание задачи).
- Отправляет данные на внешний API.
- Возвращает клиенту ответ от 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. Не забывайте изучать документацию внешних сервисов и тестировать каждую интеграцию, чтобы избежать неожиданных проблем.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ