使用Python Dependency Injector构建Aiohttp REST API应用教程

使用Python Dependency Injector构建Aiohttp REST API应用教程

python-dependency-injector Dependency injection framework for Python python-dependency-injector 项目地址: https://gitcode.com/gh_mirrors/py/python-dependency-injector

概述

本教程将指导您如何使用Python Dependency Injector框架构建一个基于aiohttp的REST API应用。我们将创建一个名为"Giphy导航器"的应用,它能够搜索Giphy上的搞笑GIF图片并返回JSON格式的结果。

项目目标

我们将构建一个具有以下功能的REST API应用:

  1. 接收客户端请求,包含搜索关键词和结果数量限制
  2. 连接Giphy API进行搜索
  3. 返回包含以下内容的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

总结

通过本教程,我们实现了:

  1. 使用aiohttp构建REST API应用
  2. 使用Python Dependency Injector管理依赖
  3. 集成Giphy API进行GIF搜索
  4. 实现服务层抽象
  5. 配置管理和依赖注入
  6. 基本测试覆盖

这种架构设计使得应用更加模块化、可测试和易于维护,是构建现代Python Web应用的优秀实践。

python-dependency-injector Dependency injection framework for Python python-dependency-injector 项目地址: https://gitcode.com/gh_mirrors/py/python-dependency-injector

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

柏赢安Simona

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

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

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

打赏作者

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

抵扣说明:

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

余额充值