使用Python Dependency Injector构建Aiohttp REST API应用教程
概述
本教程将指导您如何使用Python Dependency Injector框架构建一个基于aiohttp的REST API应用。我们将创建一个名为"Giphy导航器"的应用,它能够搜索Giphy上的搞笑GIF图片并返回JSON格式的结果。
项目目标
我们将构建一个具有以下功能的REST API应用:
- 接收客户端请求,包含搜索关键词和结果数量限制
- 连接Giphy API进行搜索
- 返回包含以下内容的JSON响应:
- 搜索关键词
- 结果数量限制
- GIF图片URL列表
环境准备
首先需要设置开发环境:
mkdir giphynav-aiohttp-tutorial
cd giphynav-aiohttp-tutorial
python3 -m venv venv
source venv/bin/activate
项目结构
创建以下项目结构:
./
├── giphynavigator/
│ ├── __init__.py
│ ├── application.py
│ ├── containers.py
│ └── handlers.py
├── venv/
└── requirements.txt
安装依赖
在requirements.txt中添加以下依赖:
dependency-injector
aiohttp
pyyaml
pytest-aiohttp
pytest-cov
然后安装:
pip install -r requirements.txt
最小化应用实现
创建基础处理器
在handlers.py中创建基础请求处理器:
from aiohttp import web
async def index(request: web.Request) -> web.Response:
query = request.query.get("query", "Dependency Injector")
limit = int(request.query.get("limit", 10))
return web.json_response({
"query": query,
"limit": limit,
"gifs": [],
})
创建依赖容器
在containers.py中创建空的依赖容器:
from dependency_injector import containers
class Container(containers.DeclarativeContainer):
pass
创建应用工厂
在application.py中创建应用工厂:
from aiohttp import web
from .containers import Container
from . import handlers
def create_app() -> web.Application:
container = Container()
app = web.Application()
app.container = container
app.add_routes([web.get("/", handlers.index)])
return app
if __name__ == "__main__":
app = create_app()
web.run_app(app)
集成Giphy API
创建Giphy客户端
创建giphy.py模块实现Giphy API客户端:
from aiohttp import ClientSession, ClientTimeout
class GiphyClient:
API_URL = "https://api.giphy.com/v1"
def __init__(self, api_key, timeout):
self._api_key = api_key
self._timeout = ClientTimeout(timeout)
async def search(self, query, limit):
url = f"{self.API_URL}/gifs/search"
params = {
"q": query,
"api_key": self._api_key,
"limit": limit
}
async with ClientSession(timeout=self._timeout) as session:
async with session.get(url, params=params) as response:
if response.status != 200:
response.raise_for_status()
return await response.json()
配置容器
更新containers.py配置依赖:
from dependency_injector import containers, providers
from . import giphy
class Container(containers.DeclarativeContainer):
config = providers.Configuration(yaml_files=["config.yml"])
giphy_client = providers.Factory(
giphy.GiphyClient,
api_key=config.giphy.api_key,
timeout=config.giphy.request_timeout
)
添加配置文件
创建config.yml配置文件:
giphy:
request_timeout: 10
实现搜索服务
创建services.py模块:
from .giphy import GiphyClient
class SearchService:
def __init__(self, giphy_client: GiphyClient):
self._giphy_client = giphy_client
async def search(self, query, limit):
if not query:
return []
result = await self._giphy_client.search(query, limit)
return [{"url": gif["url"]} for gif in result["data"]]
更新容器配置:
from . import giphy, services
class Container(containers.DeclarativeContainer):
# ...其他配置...
search_service = providers.Factory(
services.SearchService,
giphy_client=giphy_client
)
实现依赖注入
更新handlers.py使用依赖注入:
from aiohttp import web
from dependency_injector.wiring import Provide, inject
from .services import SearchService
from .containers import Container
@inject
async def index(
request: web.Request,
search_service: SearchService = Provide[Container.search_service]
) -> web.Response:
query = request.query.get("query", "Dependency Injector")
limit = int(request.query.get("limit", 10))
gifs = await search_service.search(query, limit)
return web.json_response({
"query": query,
"limit": limit,
"gifs": gifs
})
重构配置
将硬编码值移到配置中:
更新config.yml:
giphy:
request_timeout: 10
default:
query: "Dependency Injector"
limit: 10
更新handlers.py:
@inject
async def index(
request: web.Request,
search_service: SearchService = Provide[Container.search_service],
default_query: str = Provide[Container.config.default.query],
default_limit: int = Provide[Container.config.default.limit.as_int()]
) -> web.Response:
query = request.query.get("query", default_query)
limit = int(request.query.get("limit", default_limit))
# ...其余代码...
测试实现
创建tests.py模块:
import pytest
from aiohttp import web
from aiohttp.test_utils import TestClient, TestServer
from .application import create_app
@pytest.fixture
def app():
return create_app()
@pytest.fixture
async def client(app, aiohttp_client):
return await aiohttp_client(app)
async def test_index(client):
resp = await client.get("/")
assert resp.status == 200
data = await resp.json()
assert "query" in data
assert "limit" in data
assert "gifs" in data
总结
通过本教程,我们实现了:
- 使用aiohttp构建REST API应用
- 使用Python Dependency Injector管理依赖
- 集成Giphy API进行GIF搜索
- 实现服务层抽象
- 配置管理和依赖注入
- 基本测试覆盖
这种架构设计使得应用更加模块化、可测试和易于维护,是构建现代Python Web应用的优秀实践。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考