在发送请求之前,在 aiohttp 中的 post 请求中获取准备好的数据

Евг*_*лов 5 python python-asyncio aiohttp

我使用 aiohttp 来发出 post 请求。我需要在发送请求之前获取post请求中准备好的数据。因此,我尝试在 aiohttp 中找到一种与requests 库中的准备方法类似的方法。是否可以?

一年又*_*又一年 0

这只是 aiohttp 中的一个概念验证解决方法,用于在发送请求之前获取请求的正文,并同时执行操作(例如通常签名和添加签名标头)。

免责声明:我不建议在生产环境(或严肃的场合)中使用它。因为它依赖于 aiohttp 中非公开且未记录的函数或属性。如果 aiohttp 的内部实现发生变化,它可能随时崩溃。

import aiohttp
import asyncio
import hashlib
import hmac

SECRET_KEY = '__SECRET_KEY__'


class HMACBodySignClientRequest(aiohttp.ClientRequest):
    def update_body_from_data(self, body) -> None:
        """bad: hook this internal method to get body and inject signature (may fail anytime if internal changes)"""
        super().update_body_from_data(body)

        if body is None:  # use `body` rather than `self.body` is expected.
            return

        body = self.body._value or b''  # bad: access to protected property (may not available)
        signature = hmac.new(SECRET_KEY.encode('utf-8'), body, digestmod=hashlib.sha256)
        self.headers['X-Signature'] = signature.hexdigest()


async def aiohttp_body_sign_workaround():
    url = 'https://httpbin.org/api'
    data = {'foo': 'bar'}
    async with aiohttp.ClientSession(
            request_class=HMACBodySignClientRequest) as session:  # <-- auto add signature via `request_class`
        async with session.post(url=url, json=data) as response:
            print(response.request_info)


asyncio.run(aiohttp_body_sign_workaround())

Run Code Online (Sandbox Code Playgroud)

在 aiohttp 3.8.5 上测试,在任何其他版本中可能会失败。

请注意,这种方法是一种“黑魔法”,可能只适用于某些微不足道的情况,例如小数据或非流请求。使用风险自负。


它是如何工作的?

在 aiohttp 当前的内部设计中,有一个内部属性_request_class: ClientRequeston ClientSession,它委托实际的请求生成。

在某些时候,请求主体字节可能可以获取。

发现调用该ClientRequest方法后,访问可能会显示主体字节。update_body_from_data()self.body._value

幸运的是,可以在使用参数_request_class初始化 a 时指定该属性。这意味着我们可以子类化并覆盖/挂钩该方法以添加自定义逻辑(在本例中,将主体的 HMAC 签名添加到标头)。ClientSessionrequest_classClientRequestupdate_body_from_data()

是的,它依赖于内部细节,所以正如我所说,这只是一个概念验证。