Спочатку освіжимо в пам'яті, що таке асинхронне програмування.
У світі 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. Ми вже на шляху до створення по-справжньому інтегрованих додатків! 🚀
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ