在 Python 中调用 API 的现代方法 HTTP 库 响应数据验证 API 客户端的创建 调用 API 的良好方法

2025-06-08

在 Python 中调用 API 的现代方法

HTTP 库

响应数据验证

创建 API 客户端

调用 API 的好方法

尽管我最初对此持怀疑态度,但现在我坚信 Python 中的异步将塑造这门语言的未来。Python 的生态系统越来越发达,许多优秀的人才正在加入异步的行列,帮助 Python 开发者掌握这种新的编码方式。当然,编写异步代码比同步代码更复杂,但在过去几年中,很多事情发生了变化,入门门槛比以往任何时候都低。

在 Strio,我们主要使用 FastAPI 作为前端 API。它是一个异步 Web 框架,让我们能够轻松实现良好的性能。在前端、其他客户端或直接来自客户的请求期间,我们通常需要调用许多其他方:外部 API、AMQP 代理、SQL 或 NoSQL 数据库……我们的 API 充当着各种服务之间的粘合剂,由于所有这些调用都相当长,我们可以利用异步代码来为客户保持卓越的性能。

在我们调用的服务中,有很多 HTTP 请求需要发送。在当今的微服务时代,这通常符合您对前端 API 的期望:当需要执行一些相当复杂的操作时,只需调用负责该操作的服务并让它执行即可。因此,让我们尝试构建一种与这些 HTTP API 进行异步通信的现代方式。

HTTP 库

我们需要的第一个模块是 HTTP 库。为什么要使用 HTTP 库?HTTP 是一个标准,你无法真正避免它,但是这个协议有时很难实现,而且参考资料中充满了技巧。幸运的是,有些人为 Python 编写了一些很棒的 HTTP 库,我们可以使用它。

我真正喜欢的是httpx,它是由encode 团队创建的,你可以在其中找到一些最著名的 Python 开发者。httpx本质上是请求,但增加了异步支持、类型等等。它的简单语法灵感来源于请求,这使得它非常易于使用和理解,尤其对于那些刚接触异步代码并需要阅读它的人来说。

让我们看一下如何GET异步地发出简单请求:

>>> import httpx
>>> async with httpx.AsyncClient() as client:
...     r = await client.get('https://ifconfig.co/json')
...
>>> r
<Response [200 OK]>
Enter fullscreen mode Exit fullscreen mode

这是 Asyncio 解释器,启动时python -m asyncio

我介绍的这个砖块的目标是进行 HTTP 调用并以类型化和验证的模型返回响应。90% 的时间我们需要访问需要验证的响应的 JSON 主体。

响应数据验证

下一个关键点是 HTTPX 对响应主体的数据进行验证。为什么需要验证?这不仅有助于开发人员构建响应的类型,这对于自动补全和提高开发人员的工作效率大有裨益,而且还能在进一步操作之前验证数据。如果 API 返回了意外结果,我们希望立即返回一个友好的异常,而不是AttributeError在请求的后期才返回。

为了完成这项工作,我选择了pydantic,这是一个非常棒的库,可以简化数据的检查和验证。让我们看看如何将它集成到我们的请求管道中:

>>> from pydantic import BaseModel
>>> from ipaddress import IPv4Address
>>> class IfConfig(BaseModel):
...     ip: IPv4Address
...     ip_decimal: int
... 
>>> ifconfig = IfConfig.parse_obj(r.json())
>>> ifconfig
IfConfig(ip=IPv4Address('78.153.21.75'), ip_decimal=1807729179)
Enter fullscreen mode Exit fullscreen mode

现在我们已经为响应数据建立了静态类型,我们知道ifconfig.ip存在并且是IPv4Address。然而,数据也经过了验证,因此,如果ip响应中缺少该字段,或者我们尝试解析 IPv6 等类型的数据,我们就会抛出异常ValidationError,然后我们就可以针对这种异常事件采取措施。

创建 API 客户端

为了将这两块砖粘合在一起,我们可以将它们集成到一个名为 API 客户端的类中。

这个类的作用是抽象出代码库不同部分的 API 引用逻辑,这样开发者就可以忽略底层,只关注他们想要获取或修改的资源。这个类还允许我们将外部 API 的定义分离到一个地方,以便于测试和重构。

让我们创建我们的IfConfigClient代码,以便我们代码中的所有内容,从我们自己的 API 到我们的后台作业都可以轻松查询此服务:

from ipaddress import IPv4Address

from pydantic import BaseModel
from httpx import AsyncClient

IFCONFIG_URL = "https://ifconfig.co/"

class IfConfig(BaseModel):
    ip: IPv4Address
    ip_decimal: int

class IfConfigClient(AsyncClient):
    def __init__(self):
        super().__init__(base_url=IFCONFIG_URL)

    async def get_ifconfig(self):
        request = await self.get('json')

        try:
            ifconfig = IfConfig.parse_obj(request.json())
        except ValidationError:
            print("Something went wrong!")

        return ifconfig
Enter fullscreen mode Exit fullscreen mode

这里的技巧是让我们的客户端继承自AsyncClienthttpx。这使得开发和使用都变得非常简单。我们将这段代码保存在一个ifconfig.py文件中,以便我们可以在python -m asyncio解释器中导入它:

>>> from ifconfig import IfConfigClient
>>> async with IfConfigClient() as client:
...     ifconfig = await client.get_ifconfig()
... 
>>> ifconfig
IfConfig(ip=IPv4Address('78.153.21.75'), ip_decimal=1807729179)
Enter fullscreen mode Exit fullscreen mode

调用 API 的好方法

我们构建了一个相当现代化的 API 客户端:它是异步的,支持输入并验证数据。此外,出于多种原因,我希望未来人们也能以这种方式编写 API 包装器。

关注点分离

通过这种配置,API 对用户完全不透明,可以单独进行正确测试。如果 API 本身发生变化,例如某个端点新增了参数,则可以全局更改,而不会破坏当前使用此端点的所有代码。我们在 Python 生态系统中已经实践了相当长一段时间,但现在创建客户端变得更加简单。

模拟测试

我非常喜欢对复杂函数进行单元测试,但是拦截代码中所有发出的数据,比如 HTTP 调用,通常会很麻烦。多亏了我们的代理,我们有一种标准的方法来修补测试,甚至可以通过输入以下内容来创建 Fixture:

from unittest.mock import patch

import ifconfig

mock_ip = IPv4Address('78.153.21.75')
mock_ip_decimal = 1807729179
mock_ifconfig = ifconfig.IfConfig(ip=mock_ip, ip_decimal=mock_ip_decimal)

@patch.object(ifconfig.IfConfigClient, "get_ifconfig", return_value=mock_ifconfig)
def test_ifconfig_processing(get_ifconfig):
    ...
    assert get_ifconfig.assert_awaited_once()
    ...
Enter fullscreen mode Exit fullscreen mode

随处打字

客户端现已集成类型功能:您无需再费力翻阅 JSON 和字典来获取数据。所有可从请求响应中访问的字段,均可在您常用的 IDE 中自动补全,并包含其类型。与mypypyright结合使用,它还允许您在测试和提交之前执行静态类型检查。

数据验证

现在,API 客户端可以自行处理 API 响应中出现的异常情况(例如某个字段消失),而不是在其他人编写的代码中访问该字段时出现问题。这对于测试至关重要,而且在 API 发生变更时,也有利于代码的可维护性。

我很高兴看到像httpxpydantic这样的新工具在 Python 领域涌现,我认为它们让构建简洁的代码变得更容易,并为复杂的代码库强制执行良好的标准。这种编写外部请求的新方式将会推广。就连 Elasticsearch 的 Python 维护人员在讨论其 Python 客户端的未来时也表示赞同。

鏂囩珷鏉ユ簮锛�https://dev.to/fuegoio/the-modern-way-to-call-apis-in-python-2emh
PREV
磨练数据结构和算法技能的热门 YouTube 频道
NEXT
像我五岁一样解释非阻塞 I/O 简介您自己的表工厂非阻塞 I/O 实现的好处最终想法