Django实战系列(十六):Django 调用外部http服务

在 Django 中调用外部 HTTP 服务获取数据是常见需求,可通过多种方式实现。以下是详细的实现方法、最佳实践及完整示例:

一、使用 Python 内置库(urllib

适合简单请求,无需额外依赖:

from urllib import request
import json

def fetch_data():
    try:
        url = "https://api.example.com/data"
        response = request.urlopen(url)
        data = json.loads(response.read().decode("utf-8"))
        return data
    except Exception as e:
        print(f"请求失败: {e}")
        return None

二、使用第三方库(推荐 requests

1. 安装依赖
pip install requests
2. 基础用法
import requests

def fetch_data():
    try:
        url = "https://api.example.com/data"
        response = requests.get(url)
        response.raise_for_status()  # 检查请求是否成功
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"请求失败: {e}")
        return None
3. 带参数的请求
def fetch_data_with_params():
    url = "https://api.example.com/search"
    params = {"query": "python", "page": 1}
    headers = {"Authorization": "Bearer your_token"}
    
    response = requests.get(url, params=params, headers=headers)
    return response.json()
4. POST 请求示例
def post_data():
    url = "https://api.example.com/submit"
    data = {"name": "John", "age": 30}
    headers = {"Content-Type": "application/json"}
    
    response = requests.post(url, json=data, headers=headers)
    return response.json()

三、异步请求(使用 aiohttp 或 requests-futures

1. 使用 aiohttp(异步非阻塞)
pip install aiohttp
import asyncio
import aiohttp

async def fetch_data_async():
    async with aiohttp.ClientSession() as session:
        async with session.get("https://api.example.com/data") as response:
            return await response.json()

# 在视图中调用(需使用 async/await)
async def my_view(request):
    data = await fetch_data_async()
    return JsonResponse(data)
2. 使用 requests-futures(线程池异步)
pip install requests-futures
from requests_futures.sessions import FuturesSession

def fetch_data_async():
    session = FuturesSession(max_workers=5)
    future = session.get("https://api.example.com/data")
    response = future.result()  # 阻塞直到获取结果
    return response.json()

四、在 Django 视图中集成

1. 同步视图示例
from django.http import JsonResponse
import requests

def api_view(request):
    try:
        response = requests.get("https://api.example.com/data")
        response.raise_for_status()
        return JsonResponse(response.json())
    except requests.exceptions.RequestException as e:
        return JsonResponse({"error": str(e)}, status=500)
2. 异步视图示例(Django 3.1+)
from django.http import JsonResponse
import aiohttp
import asyncio

async def async_api_view(request):
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get("https://api.example.com/data") as response:
                data = await response.json()
                return JsonResponse(data)
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=500)

五、错误处理与重试机制

1. 完整错误处理
import requests
from requests.exceptions import Timeout, HTTPError, RequestException

def fetch_data_safe():
    try:
        response = requests.get(
            "https://api.example.com/data",
            timeout=5,  # 设置超时时间
            headers={"User-Agent": "MyDjangoApp/1.0"}
        )
        response.raise_for_status()  # 检查HTTP状态码
        return response.json()
    except Timeout:
        print("请求超时")
        return None
    except HTTPError as e:
        print(f"HTTP错误: {e.response.status_code}")
        return None
    except RequestException as e:
        print(f"请求异常: {e}")
        return None
2. 自动重试机制(使用 requests + tenacity
pip install tenacity
from tenacity import retry, stop_after_attempt, wait_fixed

@retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
def fetch_data_with_retry():
    response = requests.get("https://api.example.com/data")
    response.raise_for_status()
    return response.json()

六、性能优化

1. 连接池管理
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

def get_session():
    session = requests.Session()
    retries = Retry(total=3, backoff_factor=1)
    session.mount("https://", HTTPAdapter(max_retries=retries))
    return session

# 在视图中使用
def my_view(request):
    session = get_session()
    response = session.get("https://api.example.com/data")
    return JsonResponse(response.json())
2. 缓存机制(减少重复请求)
from django.core.cache import cache

def cached_fetch_data():
    data = cache.get("external_api_data")
    if data is None:
        response = requests.get("https://api.example.com/data")
        data = response.json()
        cache.set("external_api_data", data, 60 * 10)  # 缓存10分钟
    return data

七、安全注意事项

  1. 敏感信息保护

    • 不要在代码中硬编码 API 密钥或密码
    • 使用环境变量或 Django 配置文件存储敏感信息
  2. 输入验证

    • 验证外部 API 返回的数据格式
    • 使用 serializers 或 pydantic 进行数据验证
  3. 防止 DOS 攻击

    • 设置合理的超时时间(建议 5-10 秒)
    • 限制并发请求数量
    • 实现请求频率限制

八、生产环境建议

1)使用专用服务层

# services/external_api.py
import requests

class ExternalAPIService:
    def __init__(self):
        self.base_url = "https://api.example.com"
        self.session = requests.Session()
        self.session.headers.update({"Authorization": "Bearer your_token"})
    
    def get_data(self, endpoint):
        response = self.session.get(f"{self.base_url}/{endpoint}")
        response.raise_for_status()
        return response.json()

2)异步任务处理

  • 使用 Celery 将耗时的 API 调用放入后台队列
  • 避免阻塞 Django 请求处理线程
  1. 监控与日志

    • 记录 API 请求响应时间和错误
    • 集成 Sentry 等监控工具捕获异常

九、完整示例:天气 API 调用

# views.py
from django.http import JsonResponse
import requests
from django.views.decorators.cache import cache_page

@cache_page(60 * 15)  # 缓存15分钟
def weather_view(request, city):
    api_key = "your_api_key"  # 从环境变量获取
    url = f"https://api.openweathermap.org/data/2.5/weather"
    params = {
        "q": city,
        "appid": api_key,
        "units": "metric"
    }
    
    try:
        response = requests.get(url, params=params)
        response.raise_for_status()
        data = response.json()
        
        # 提取需要的数据
        weather_data = {
            "city": data["name"],
            "temperature": data["main"]["temp"],
            "description": data["weather"][0]["description"],
            "humidity": data["main"]["humidity"]
        }
        
        return JsonResponse(weather_data)
    except requests.exceptions.RequestException as e:
        return JsonResponse({"error": str(e)}, status=500)

总结

  • 简单请求:使用 requests 库
  • 异步需求:使用 aiohttp 或 requests-futures
  • 重试机制:结合 tenacity 库实现
  • 性能优化:使用连接池和缓存
  • 安全保障:验证输入、保护敏感信息、设置超时

合理选择请求方式并做好错误处理,可以在 Django 中高效、安全地调用外部 HTTP 服务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

alden_ygq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值