Для начала давайте освежим в памяти, что такое асинхронное программирование.
В мире Python асинхронность достигается с использованием ключевых слов async и await.
Эти магические слова позволяют нам писать неблокирующий код, который может масштабироваться для обработки тысяч запросов одновременно.
Представьте повара, который может одновременно готовить блюда для сотен клиентов (асинхронность), вместо того чтобы готовить только одно блюдо за раз, дожидаясь завершения процесса (синхронность).
Асинхронный подход особенно полезен при работе с сетевыми операциями, такими как HTTP-запросы.
В FastAPI асинхронность – это фундаментальная часть. Если мы используем асинхронные функции в качестве эндпоинтов и для взаимодействия с внешними API, мы уменьшаем блокировку и ускоряем обработку запросов.
Отправка асинхронных GET-запросов с помощью httpx
Библиотека httpx поддерживает выполнение асинхронных HTTP-запросов, что делает её идеальным выбором для работы в асинхронных приложениях, таких как FastAPI. Давайте разберём, как это работает.
Приведём простой пример асинхронного GET-запроса.
Допустим, нам нужно получить список пользователей с публичного API. Вот как это делается с использованием httpx:
import httpx
async def fetch_users():
async with httpx.AsyncClient() as client:
response = await client.get('https://jsonplaceholder.typicode.com/users')
# Проверяем статус ответа
if response.status_code == 200:
# Возвращаем данные в JSON формате
return response.json()
else:
raise Exception(f"Ошибка запроса: {response.status_code}")
Итак, как это работает:
- Мы используем
httpx.AsyncClientдля выполнения запросов. - Метод
await client.get()отправляет асинхронный GET-запрос. - Мы проверяем статус ответа (200 означает успех).
- Если всё хорошо, мы возвращаем данные в формате JSON.
Создаём эндпоинт в FastAPI
Теперь давайте завернём наш асинхронный запрос в эндпоинт FastAPI.
Предположим, наше приложение должно иметь эндпоинт /users, который возвращает список пользователей из внешнего API.
Вот как это сделать:
from fastapi import FastAPI, HTTPException
import httpx
app = FastAPI()
@app.get("/users")
async def get_users():
async with httpx.AsyncClient() as client:
response = await client.get('https://jsonplaceholder.typicode.com/users')
if response.status_code == 200:
return response.json()
else:
raise HTTPException(status_code=response.status_code, detail="Ошибка при запросе внешнего API")
Здесь мы создаём эндпоинт, который:
- Отправляет GET-запрос к внешнему API.
- Если запрос успешен, возвращает полученные данные пользователю.
- Если запрос не прошёл, выбрасывает исключение
HTTPException, которое FastAPI преобразует в читабельный HTTP-ответ.
Обработка ответа от внешнего API
Иногда данные, полученные от внешнего API, могут быть "не самыми красивыми". Например, они могут содержать лишнюю информацию. В таких случаях мы можем предварительно обработать ответ перед отправкой пользователю.
Допустим, нам нужно вернуть только имена пользователей из списка. Вот как это можно сделать:
@app.get("/users")
async def get_users():
async with httpx.AsyncClient() as client:
response = await client.get('https://jsonplaceholder.typicode.com/users')
if response.status_code == 200:
users = response.json()
# Возвращаем только имена пользователей
return {"usernames": [user["name"] for user in users]}
else:
raise HTTPException(status_code=response.status_code, detail="Ошибка при запросе внешнего API")
Теперь эндпоинт вернёт не полный объект пользователя, а только их имена.
Обработка ошибок
Работая с внешними API, мы должны учитывать возможные ошибки, такие как неправильный URL, истёкший токен или недоступность сервера. Если мы просто проигнорируем такие ситуации, пользователь нашего приложения получит неполноценный или некорректный ответ.
Для базовой обработки ошибок можно использовать try-except. Вот пример:
@app.get("/users")
async def get_users():
try:
async with httpx.AsyncClient() as client:
response = await client.get('https://jsonplaceholder.typicode.com/users')
response.raise_for_status() # Выбрасывает ошибку, если статус-код не 2xx
return response.json()
except httpx.RequestError as exc:
raise HTTPException(status_code=500, detail=f"Ошибка соединения: {exc}")
except httpx.HTTPStatusError as exc:
raise HTTPException(status_code=exc.response.status_code, detail=f"Ошибка HTTP: {exc.response.content}")
Здесь мы:
- Используем
response.raise_for_status()для проверки успешности запроса. - Перехватываем сетевые ошибки (
httpx.RequestError). - Обрабатываем ошибки статуса HTTP (
httpx.HTTPStatusError).
Практический пример: Интеграция с внешним API
Теперь создадим полноценный мини-сервис для получения данных о погоде из внешнего API (например, OpenWeatherMap).
Регистрация и получение API-ключа:
- Перейдите на сайт OpenWeatherMap.
- Зарегистрируйтесь и создайте API-ключ.
Вот пример реализации эндпоинта:
from fastapi import FastAPI, HTTPException
import httpx
app = FastAPI()
API_KEY = "ваш_api_ключ"
@app.get("/weather/{city}")
async def get_weather(city: str):
url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}&units=metric"
try:
async with httpx.AsyncClient() as client:
response = await client.get(url)
response.raise_for_status()
data = response.json()
# Упрощаем данные перед отправкой
return {
"city": data["name"],
"temperature": data["main"]["temp"],
"description": data["weather"][0]["description"]
}
except httpx.RequestError as exc:
raise HTTPException(status_code=500, detail=f"Ошибка соединения: {exc}")
except httpx.HTTPStatusError as exc:
raise HTTPException(status_code=exc.response.status_code, detail="Город не найден!")
Проверка работы
- Запустите приложение
uvicorn main:app --reload. - Перейдите в браузере по адресу
http://127.0.0.1:8000/weather/London. Вы увидите текущую погоду в Лондоне.
Разбор типичных ошибок
- Асинхронность не включена. Если вы забудете добавить
asyncв функцию илиawaitпри вызове метода, получите ошибку "coroutine was never awaited". - Неверный API ключ. Если API-ключ отсутствует или указан неправильно, сервис вернёт сообщение об ошибке. Убедитесь, что передали ключ через параметр
appid. - Обработка исключений. Если ваши запросы падают (например, из-за недоступного интернета), без обработки ошибок приложение "сломается". Включите
try-except!
Теперь вы умеете отправлять асинхронные GET-запросы и обрабатывать данные, полученные из внешних API. Мы уже на пути к созданию по-настоящему интегрированных приложений! 🚀
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ